mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 12:46:58 +00:00
1
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
//========= 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_capture_point.h
|
||||
// Move to and try to capture the next point
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_BOT_CAPTURE_POINT_H
|
||||
#define TF_BOT_CAPTURE_POINT_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotCapturePoint : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
|
||||
virtual const char *GetName( void ) const { return "CapturePoint"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_CAPTURE_POINT_H
|
||||
@@ -0,0 +1,442 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_defend_point.h
|
||||
// Move to and defend current point from capture
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh/tf_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_defend_point.h"
|
||||
#include "bot/behavior/scenario/capture_point/tf_bot_capture_point.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
#include "bot/behavior/tf_bot_seek_and_destroy.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h"
|
||||
#include "bot/behavior/sniper/tf_bot_sniper_attack.h"
|
||||
#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
extern ConVar tf_bot_min_setup_gate_defend_range;
|
||||
extern ConVar tf_bot_max_setup_gate_defend_range;
|
||||
extern ConVar tf_bot_min_setup_gate_sniper_defend_range;
|
||||
extern ConVar tf_bot_offense_must_push_time;
|
||||
|
||||
ConVar tf_bot_defense_must_defend_time( "tf_bot_defense_must_defend_time", "300", FCVAR_CHEAT, "If timer is less than this, bots will stay near point and guard" );
|
||||
ConVar tf_bot_max_point_defend_range( "tf_bot_max_point_defend_range", "1250", FCVAR_CHEAT, "How far (in travel distance) from the point defending bots will take up positions" );
|
||||
ConVar tf_bot_defense_debug( "tf_bot_defense_debug", "0", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPoint::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
m_defenseArea = NULL;
|
||||
|
||||
// higher skilled bots prefer to seek and destroy until the time is almost up
|
||||
static float roamChance[ CTFBot::NUM_DIFFICULTY_LEVELS ] = { 10.0f, 50.0f, 75.0f, 90.0f };
|
||||
m_isAllowedToRoam = ( RandomFloat( 0.0f, 100.0f ) < roamChance[ (int)clamp( me->GetDifficulty(), CTFBot::EASY, CTFBot::EXPERT ) ] );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Return true if we're in immediate danger of losing the point
|
||||
*/
|
||||
bool CTFBotDefendPoint::IsPointThreatened( CTFBot *me )
|
||||
{
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
|
||||
if ( point == NULL )
|
||||
return false;
|
||||
|
||||
if ( point->LastContestedAt() > 0.0f && ( gpGlobals->curtime - point->LastContestedAt() ) < 5.0f )
|
||||
{
|
||||
// the point is, or was very recently, contested
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we just lost a point, we should fall back and stand on the next point to defend against a rush
|
||||
if ( me->WasPointJustLost() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
// if an enemy is closer to the point than we are, head them off
|
||||
// TODO: Compare time to reach, not distance
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat )
|
||||
{
|
||||
const float tolerance = 100.0f;
|
||||
|
||||
float themRange = ( threat->GetLastKnownPosition() - point->GetAbsOrigin() ).Length();
|
||||
float myRange = ( me->GetAbsOrigin() - point->GetAbsOrigin() ).Length();
|
||||
if ( myRange + tolerance > themRange )
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Are we smart enough to get on the point to block the cap
|
||||
bool CTFBotDefendPoint::WillBlockCapture( CTFBot *me ) const
|
||||
{
|
||||
if ( TFGameRules()->IsInTraining() )
|
||||
return false;
|
||||
|
||||
if ( me->IsDifficulty( CTFBot::EASY ) )
|
||||
return false;
|
||||
|
||||
if ( me->IsDifficulty( CTFBot::NORMAL ) )
|
||||
{
|
||||
// 50% chance of blocking cap
|
||||
return me->TransientlyConsistentRandomValue() > 0.5f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPoint::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// King of the Hill logic
|
||||
CTeamControlPointMaster *master = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
|
||||
if ( master && master->GetNumPoints() == 1 )
|
||||
{
|
||||
// if we don't own the only point, switch to capture behavior
|
||||
CTeamControlPoint *point = master->GetControlPoint( 0 );
|
||||
if ( point && point->GetOwner() != me->GetTeamNumber() )
|
||||
{
|
||||
return ChangeTo( new CTFBotCapturePoint, "We need to capture the point!" );
|
||||
}
|
||||
}
|
||||
|
||||
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 CTFBotCapturePoint, "We need to capture our point(s)" );
|
||||
}
|
||||
|
||||
// if point in is danger - get ON the point!
|
||||
// Don't do this in training to keep things easy for the new trainee
|
||||
if ( IsPointThreatened( me ) && WillBlockCapture( me ) )
|
||||
{
|
||||
// point is being captured - get on it!
|
||||
return SuspendFor( new CTFBotDefendPointBlockCapture, "Moving to block point capture!" );
|
||||
}
|
||||
|
||||
// point is safe for the moment
|
||||
|
||||
// if I'm uber'd, go get 'em!
|
||||
if ( me->m_Shared.InCond( TF_COND_INVULNERABLE ) )
|
||||
{
|
||||
const float uberChargeTime = 6.0;
|
||||
return SuspendFor( new CTFBotSeekAndDestroy( uberChargeTime ), "Attacking because I'm uber'd!" );
|
||||
}
|
||||
|
||||
if ( point && point->IsLocked() )
|
||||
{
|
||||
return SuspendFor( new CTFBotSeekAndDestroy, "Seek and destroy until the point unlocks" );
|
||||
}
|
||||
|
||||
if ( m_isAllowedToRoam && me->GetTimeLeftToCapture() > tf_bot_defense_must_defend_time.GetFloat() )
|
||||
{
|
||||
return SuspendFor( new CTFBotSeekAndDestroy( 15.0f ), "Seek and destroy - we have lots of time" );
|
||||
}
|
||||
|
||||
if ( TFGameRules()->InSetup() )
|
||||
{
|
||||
// don't lose patience during setup time
|
||||
m_idleTimer.Reset();
|
||||
}
|
||||
|
||||
// if we see an enemy as we have a melee weapon equipped, chase them down
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// we're aware of an enemy
|
||||
m_idleTimer.Reset();
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_PYRO ) )
|
||||
{
|
||||
// go get 'em
|
||||
return SuspendFor( new CTFBotSeekAndDestroy( 15.0f ), "Going after an enemy" );
|
||||
}
|
||||
|
||||
CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
if ( myWeapon && ( myWeapon->IsMeleeWeapon() || myWeapon->IsWeapon( TF_WEAPON_FLAMETHROWER ) ) )
|
||||
{
|
||||
// TODO: Check if threat is visible and if not, move to last known position
|
||||
CTFBotPathCost cost( me, me->IsPlayerClass( TF_CLASS_PYRO ) ? SAFEST_ROUTE : FASTEST_ROUTE );
|
||||
m_chasePath.Update( me, threat->GetEntity(), cost );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
|
||||
// choose where we'll defend from
|
||||
if ( m_defenseArea == NULL || m_idleTimer.IsElapsed() )
|
||||
{
|
||||
m_defenseArea = SelectAreaToDefendFrom( me );
|
||||
}
|
||||
|
||||
if ( m_defenseArea )
|
||||
{
|
||||
if ( me->GetLastKnownArea() == m_defenseArea )
|
||||
{
|
||||
// at our defense position
|
||||
if ( CTFBotPrepareStickybombTrap::IsPossible( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotPrepareStickybombTrap, "Laying sticky bombs!" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to our desired defense position, repathing periodically to account for changing situation
|
||||
VPROF_BUDGET( "CTFBotDefendPoint::Update( repath )", "NextBot" );
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, m_defenseArea->GetCenter(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
// we're not idle while we're moving to our defend position
|
||||
m_idleTimer.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPoint::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
// may have lost point - recheck
|
||||
me->ClearMyControlPoint();
|
||||
m_repathTimer.Invalidate();
|
||||
m_path.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
m_defenseArea = SelectAreaToDefendFrom( me );
|
||||
me->GetLocomotionInterface()->ClearStuckStatus();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
m_defenseArea = SelectAreaToDefendFrom( me );
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryContested( CTFBot *me, int territoryID )
|
||||
{
|
||||
// handled in the Update() loop
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryCaptured( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPoint::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
// we lost it, fall back to next point
|
||||
me->ClearMyControlPoint();
|
||||
m_defenseArea = SelectAreaToDefendFrom( me );
|
||||
m_repathTimer.Invalidate();
|
||||
m_path.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CSelectDefenseAreaForPoint : public ISearchSurroundingAreasFunctor
|
||||
{
|
||||
public:
|
||||
CSelectDefenseAreaForPoint( CTFNavArea *pointArea, int myTeam, CUtlVector< CTFNavArea * > *areaVector )
|
||||
{
|
||||
m_pointArea = pointArea;
|
||||
m_myTeam = myTeam;
|
||||
|
||||
// don't select areas that are beyond the point
|
||||
m_incursionFlowLimit = pointArea->GetIncursionDistance( m_myTeam ) + 250.0f;
|
||||
|
||||
m_areaVector = areaVector;
|
||||
m_areaVector->RemoveAll();
|
||||
}
|
||||
|
||||
virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)baseArea;
|
||||
|
||||
if ( !TFGameRules()->IsInKothMode() )
|
||||
{
|
||||
// don't select areas that are beyond the point
|
||||
if ( area->GetIncursionDistance( m_myTeam ) > m_incursionFlowLimit )
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( area->IsPotentiallyVisible( m_pointArea ) )
|
||||
{
|
||||
// a bit of a hack here to avoid bots choosing to defend in bottom of ravine at stage 3 of dustbowl
|
||||
const float tooLow = 220.0f;
|
||||
if ( m_pointArea->GetCenter().z - area->GetCenter().z < tooLow )
|
||||
{
|
||||
// valid defense position
|
||||
m_areaVector->AddToTail( area );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool ShouldSearch( CNavArea *adjArea, CNavArea *currentArea, float travelDistanceSoFar )
|
||||
{
|
||||
if ( adjArea->IsBlocked( TFGameRules()->IsInKothMode() ? TEAM_ANY : m_myTeam ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( travelDistanceSoFar > tf_bot_max_point_defend_range.GetFloat() )
|
||||
{
|
||||
// too far away
|
||||
return false;
|
||||
}
|
||||
|
||||
const float maxHeightChange = 65.0f;
|
||||
float deltaZ = currentArea->ComputeAdjacentConnectionHeightChange( adjArea );
|
||||
return ( fabs( deltaZ ) < maxHeightChange );
|
||||
}
|
||||
|
||||
CTFNavArea *m_pointArea;
|
||||
CUtlVector< CTFNavArea * > *m_areaVector;
|
||||
float m_incursionFlowLimit;
|
||||
int m_myTeam;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* Select the area where we will guard the point from
|
||||
*/
|
||||
CTFNavArea *CTFBotDefendPoint::SelectAreaToDefendFrom( CTFBot *me )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotDefendPoint::SelectAreaToDefendFrom", "NextBot" );
|
||||
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( !point )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// decide where we will defend from
|
||||
CUtlVector< CTFNavArea * > defenseAreas;
|
||||
|
||||
/*
|
||||
if ( !TFGameRules()->IsInKothMode() &&
|
||||
point->GetTeamCapPercentage( me->GetTeamNumber() ) <= 0.0f && // point is currently safe
|
||||
( ObjectiveResource()->GetPreviousPointForPoint( point->GetPointIndex(), me->GetTeamNumber(), 0 ) < 0 || // this is the first cap point
|
||||
me->IsPlayerClass( TF_CLASS_PYRO ) ) ) // pyros are skirmishers
|
||||
{
|
||||
if ( TheTFNavMesh()->GetSetupGateDefenseAreas() )
|
||||
{
|
||||
defenseAreas = *TheTFNavMesh()->GetSetupGateDefenseAreas();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if ( defenseAreas.Count() == 0 )
|
||||
{
|
||||
CTFNavArea *pointArea = TheTFNavMesh()->GetControlPointCenterArea( point->GetPointIndex() );
|
||||
if ( pointArea )
|
||||
{
|
||||
// search outwards from the point along walkable areas (not drop downs) to make sure we can get back to the point quickly
|
||||
CSelectDefenseAreaForPoint defenseScan( pointArea, me->GetTeamNumber(), &defenseAreas );
|
||||
SearchSurroundingAreas( pointArea, defenseScan );
|
||||
}
|
||||
}
|
||||
|
||||
// select a specific area from the potential defense set
|
||||
if ( defenseAreas.Count() == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// how long will we wait if we don't see any action
|
||||
m_idleTimer.Start( RandomFloat( 10.0f, 20.0f ) );
|
||||
|
||||
if ( tf_bot_defense_debug.GetBool() )
|
||||
{
|
||||
for( int i=0; i<defenseAreas.Count(); ++i )
|
||||
{
|
||||
defenseAreas[i]->DrawFilled( 0, 200, 200, 999.9f );
|
||||
}
|
||||
}
|
||||
|
||||
// select one of the defense areas
|
||||
int which = RandomInt( 0, defenseAreas.Count()-1 );
|
||||
return defenseAreas[ which ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_defend_point.h
|
||||
// Move to and defend current point from capture
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_BOT_DEFEND_POINT_H
|
||||
#define TF_BOT_DEFEND_POINT_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
class CTFBotDefendPoint : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual const char *GetName( void ) const { return "DefendPoint"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path; // for moving to a defense position
|
||||
ChasePath m_chasePath; // for chasing enemies
|
||||
|
||||
CountdownTimer m_repathTimer;
|
||||
CountdownTimer m_lookAroundTimer;
|
||||
CountdownTimer m_idleTimer;
|
||||
|
||||
CTFNavArea *m_defenseArea;
|
||||
CTFNavArea *SelectAreaToDefendFrom( CTFBot *me );
|
||||
|
||||
bool IsPointThreatened( CTFBot *me );
|
||||
bool WillBlockCapture( CTFBot *me ) const;
|
||||
bool m_isAllowedToRoam;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_DEFEND_POINT_H
|
||||
+247
@@ -0,0 +1,247 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_defend_point_block_capture.h
|
||||
// Move to and defend current point from capture
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh/tf_nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "trigger_area_capture.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/capture_point/tf_bot_defend_point_block_capture.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_defend_owned_point_percent( "tf_bot_defend_owned_point_percent", "0.5", FCVAR_CHEAT, "Stay on the contested point we own until enemy cap percent falls below this" );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPointBlockCapture::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
m_point = me->GetMyControlPoint();
|
||||
if ( m_point == NULL )
|
||||
{
|
||||
return Done( "Point is NULL" );
|
||||
}
|
||||
|
||||
m_defenseArea = static_cast< CTFNavArea * >( TheTFNavMesh()->GetNearestNavArea( m_point->GetAbsOrigin() ) );
|
||||
if ( m_defenseArea == NULL )
|
||||
{
|
||||
return Done( "Can't find nav area on point" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
bool CTFBotDefendPointBlockCapture::IsPointSafe( CTFBot *me )
|
||||
{
|
||||
// if a point was just captured, defend this point for awhile
|
||||
if ( me->WasPointJustLost() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( m_point == NULL )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( m_point->GetTeamCapPercentage( me->GetTeamNumber() ) < tf_bot_defend_owned_point_percent.GetFloat() )
|
||||
{
|
||||
// we're not in complete control of this point yet
|
||||
return false;
|
||||
}
|
||||
|
||||
// is point is being contested, or was just being contested, its not safe
|
||||
if ( m_point->HasBeenContested() && ( gpGlobals->curtime - m_point->LastContestedAt() ) < 5.0f )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we still see a near threat, stay put
|
||||
const CKnownEntity *knownThreat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( knownThreat )
|
||||
{
|
||||
const float dangerRange = 500.0f;
|
||||
if ( ( knownThreat->GetLastKnownPosition() - m_point->GetAbsOrigin() ).IsLengthLessThan( dangerRange ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPointBlockCapture::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// if point is safe, we can move back to our defense positions
|
||||
if ( IsPointSafe( me ) )
|
||||
{
|
||||
return Done( "Point is safe again" );
|
||||
}
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
// medics look ridiculous rushing to the point - they need to heal
|
||||
return SuspendFor( new CTFBotMedicHeal );
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
|
||||
Extent pointExtent;
|
||||
pointExtent.Init( m_point );
|
||||
|
||||
bool isStandingOnThePoint = pointExtent.Contains( me->GetAbsOrigin() );
|
||||
|
||||
const CUtlVector< CTFNavArea * > *controlPointAreas = TheTFNavMesh()->GetControlPointAreas( m_point->GetPointIndex() );
|
||||
if ( controlPointAreas )
|
||||
{
|
||||
for( int i=0; i<controlPointAreas->Count(); ++i )
|
||||
{
|
||||
if ( me->GetLastKnownArea() && me->GetLastKnownArea()->GetID() == controlPointAreas->Element(i)->GetID() )
|
||||
{
|
||||
isStandingOnThePoint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( isStandingOnThePoint && CTFBotPrepareStickybombTrap::IsPossible( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotPrepareStickybombTrap, "Placing stickies for defense" );
|
||||
}
|
||||
|
||||
if ( controlPointAreas )
|
||||
{
|
||||
// move to a random spot on this control point
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||||
|
||||
float totalArea = 0.0f;
|
||||
int i;
|
||||
for( i=0; i<controlPointAreas->Count(); ++i )
|
||||
{
|
||||
CTFNavArea *area = controlPointAreas->Element(i);
|
||||
totalArea += area->GetSizeX() * area->GetSizeY();
|
||||
}
|
||||
|
||||
float which = RandomFloat( 0.0f, totalArea - 1.0f );
|
||||
CTFNavArea *goalArea = NULL;
|
||||
for( i=0; i<controlPointAreas->Count(); ++i )
|
||||
{
|
||||
CTFNavArea *area = controlPointAreas->Element(i);
|
||||
which -= area->GetSizeX() * area->GetSizeY();
|
||||
if ( which <= 0.0f )
|
||||
{
|
||||
goalArea = area;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( goalArea )
|
||||
{
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, goalArea->GetRandomPoint(), cost );
|
||||
}
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
else if ( !isStandingOnThePoint )
|
||||
{
|
||||
// get on the point!
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, ( pointExtent.lo + pointExtent.hi )/2.0f, cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDefendPointBlockCapture::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
me->GetLocomotionInterface()->ClearStuckStatus();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryContested( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryToSustain();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryCaptured( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDefendPointBlockCapture::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
// we lost it, fall back
|
||||
return TryDone( RESULT_CRITICAL, "Lost the point" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotDefendPointBlockCapture::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
// hurry up and get on the point!
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotDefendPointBlockCapture::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
// get on the point!
|
||||
return ANSWER_NO;
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_defend_point_block_capture.h
|
||||
// Move to and defend current point from capture
|
||||
// Michael Booth, February 2009
|
||||
|
||||
#ifndef TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H
|
||||
#define TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H
|
||||
|
||||
|
||||
class CTFBotDefendPointBlockCapture : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const;
|
||||
|
||||
virtual const char *GetName( void ) const { return "BlockCapture"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
CTeamControlPoint *m_point;
|
||||
CTFNavArea *m_defenseArea;
|
||||
|
||||
bool IsPointSafe( CTFBot *me );
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_DEFEND_POINT_BLOCK_CAPTURE_H
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_attack_flag_defenders.cpp
|
||||
// Attack enemies that are preventing the flag from reaching its destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h"
|
||||
|
||||
ConVar tf_bot_flag_escort_range( "tf_bot_flag_escort_range", "500", FCVAR_CHEAT );
|
||||
|
||||
extern ConVar tf_bot_flag_escort_max_count;
|
||||
|
||||
extern int GetBotEscortCount( int team );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotAttackFlagDefenders::CTFBotAttackFlagDefenders( float minDuration )
|
||||
{
|
||||
if ( minDuration > 0.0f )
|
||||
{
|
||||
m_minDurationTimer.Start( minDuration );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_minDurationTimer.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotAttackFlagDefenders::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
m_chasePlayer = NULL;
|
||||
return CTFBotAttack::OnStart( me, priorAction );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotAttackFlagDefenders::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
if ( m_watchFlagTimer.IsElapsed() && m_minDurationTimer.IsElapsed() )
|
||||
{
|
||||
m_watchFlagTimer.Start( RandomFloat( 1.0f, 3.0f ) );
|
||||
|
||||
CCaptureFlag *flag = me->GetFlagToFetch();
|
||||
|
||||
if ( !flag )
|
||||
{
|
||||
return Done( "No flag" );
|
||||
}
|
||||
|
||||
// can't reach flag if it is at home
|
||||
if ( !TFGameRules()->IsMannVsMachineMode() || !flag->IsHome() )
|
||||
{
|
||||
CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() );
|
||||
if ( !carrier )
|
||||
{
|
||||
return Done( "Flag was dropped" );
|
||||
}
|
||||
|
||||
if ( me->IsSelf( carrier ) )
|
||||
{
|
||||
return Done( "I picked up the flag!" );
|
||||
}
|
||||
|
||||
// escort the flag carrier, unless the carrier is in a squad
|
||||
CTFBot *botCarrier = ToTFBot( carrier );
|
||||
if ( !botCarrier || !botCarrier->IsInASquad() )
|
||||
{
|
||||
if ( me->IsRangeLessThan( carrier, tf_bot_flag_escort_range.GetFloat() ) )
|
||||
{
|
||||
if ( GetBotEscortCount( me->GetTeamNumber() ) < tf_bot_flag_escort_max_count.GetInt() )
|
||||
{
|
||||
return ChangeTo( new CTFBotEscortFlagCarrier, "Near flag carrier - escorting" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionResult< CTFBot > result = CTFBotAttack::Update( me, interval );
|
||||
|
||||
if ( result.IsDone() )
|
||||
{
|
||||
// nothing to attack, move towards a random player
|
||||
|
||||
if ( m_chasePlayer == NULL || !m_chasePlayer->IsAlive() )
|
||||
{
|
||||
m_chasePlayer = me->SelectRandomReachableEnemy();
|
||||
}
|
||||
|
||||
if ( m_chasePlayer == NULL )
|
||||
{
|
||||
// everyone is dead or hiding in the spawn room - go escort the flag
|
||||
return ChangeTo( new CTFBotEscortFlagCarrier, "No reachable victim - escorting flag" );
|
||||
}
|
||||
|
||||
// cheat and "see" our victim so we know where to go
|
||||
me->GetVisionInterface()->AddKnownEntity( m_chasePlayer );
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 3.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
float maxPathLength = TFGameRules()->IsMannVsMachineMode() ? TFBOT_MVM_MAX_PATH_LENGTH : 0.0f;
|
||||
m_path.Compute( me, m_chasePlayer, cost, maxPathLength );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_attack_flag_defenders.h
|
||||
// Attack enemies that are preventing the flag from reaching its destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#ifndef TF_BOT_ATTACK_FLAG_DEFENDERS_H
|
||||
#define TF_BOT_ATTACK_FLAG_DEFENDERS_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotAttackFlagDefenders : public CTFBotAttack
|
||||
{
|
||||
public:
|
||||
CTFBotAttackFlagDefenders( float minDuration = -1.0f );
|
||||
virtual ~CTFBotAttackFlagDefenders() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "AttackFlagDefenders"; }
|
||||
|
||||
private:
|
||||
CountdownTimer m_minDurationTimer;
|
||||
CountdownTimer m_watchFlagTimer;
|
||||
CHandle< CTFPlayer > m_chasePlayer;
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ATTACK_FLAG_DEFENDERS_H
|
||||
@@ -0,0 +1,422 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_deliver_flag.cpp
|
||||
// Take the flag we are holding to its destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "tf_player_shared.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h"
|
||||
#include "bot/behavior/tf_bot_taunt.h"
|
||||
#include "bot/behavior/tf_bot_mvm_deploy_bomb.h"
|
||||
|
||||
#include "tf_objective_resource.h"
|
||||
#include "player_vs_environment/tf_population_manager.h"
|
||||
#include "econ_item_system.h"
|
||||
#include "tf_gamestats.h"
|
||||
|
||||
#include "bot/behavior/nav_entities/tf_bot_nav_ent_move_to.h"
|
||||
#include "bot/behavior/nav_entities/tf_bot_nav_ent_wait.h"
|
||||
|
||||
#include "particle_parse.h"
|
||||
|
||||
ConVar tf_mvm_bot_allow_flag_carrier_to_fight( "tf_mvm_bot_allow_flag_carrier_to_fight", "1", FCVAR_CHEAT );
|
||||
|
||||
ConVar tf_mvm_bot_flag_carrier_interval_to_1st_upgrade( "tf_mvm_bot_flag_carrier_interval_to_1st_upgrade", "5", FCVAR_CHEAT );
|
||||
ConVar tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade( "tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade", "15", FCVAR_CHEAT );
|
||||
ConVar tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade( "tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade", "15", FCVAR_CHEAT );
|
||||
|
||||
ConVar tf_mvm_bot_flag_carrier_health_regen( "tf_mvm_bot_flag_carrier_health_regen", "45.0f", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDeliverFlag::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_flTotalTravelDistance = -1.0f;
|
||||
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
if ( tf_mvm_bot_allow_flag_carrier_to_fight.GetBool() == false )
|
||||
{
|
||||
me->SetAttribute( CTFBot::SUPPRESS_FIRE );
|
||||
}
|
||||
|
||||
// mini-bosses don't upgrade - they are already tough
|
||||
if ( me->IsMiniBoss() )
|
||||
{
|
||||
m_upgradeLevel = DONT_UPGRADE;
|
||||
if ( TFObjectiveResource() )
|
||||
{
|
||||
// Set threat level to max
|
||||
TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 4 );
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_upgradeLevel = 0;
|
||||
m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_1st_upgrade.GetFloat() );
|
||||
if ( TFObjectiveResource() )
|
||||
{
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// In Mann Vs Machine, the flag carrier gets stronger the longer he carries the flag
|
||||
bool CTFBotDeliverFlag::UpgradeOverTime( CTFBot *me )
|
||||
{
|
||||
if ( TFGameRules()->IsMannVsMachineMode() && m_upgradeLevel != DONT_UPGRADE )
|
||||
{
|
||||
CTFNavArea *myArea = me->GetLastKnownArea();
|
||||
int spawnRoomFlag = me->GetTeamNumber() == TF_TEAM_RED ? TF_NAV_SPAWN_ROOM_RED : TF_NAV_SPAWN_ROOM_BLUE;
|
||||
|
||||
if ( myArea && myArea->HasAttributeTF( spawnRoomFlag ) )
|
||||
{
|
||||
// don't start counting down until we leave the spawn
|
||||
m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_1st_upgrade.GetFloat() );
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() );
|
||||
}
|
||||
|
||||
// do defensive buff effect ourselves (since we're not a soldier)
|
||||
if ( m_upgradeLevel > 0 && m_buffPulseTimer.IsElapsed() )
|
||||
{
|
||||
m_buffPulseTimer.Start( 1.0f );
|
||||
|
||||
CUtlVector< CTFPlayer * > playerVector;
|
||||
CollectPlayers( &playerVector, me->GetTeamNumber(), COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
const float buffRadius = 450.0f;
|
||||
|
||||
for( int i=0; i<playerVector.Count(); ++i )
|
||||
{
|
||||
if ( me->IsRangeLessThan( playerVector[i], buffRadius ) )
|
||||
{
|
||||
playerVector[i]->m_Shared.AddCond( TF_COND_DEFENSEBUFF_NO_CRIT_BLOCK, 1.2f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the flag carrier gets stronger the longer he holds the flag
|
||||
if ( m_upgradeTimer.IsElapsed() )
|
||||
{
|
||||
const int maxLevel = 3;
|
||||
|
||||
if ( m_upgradeLevel < maxLevel )
|
||||
{
|
||||
++m_upgradeLevel;
|
||||
|
||||
TFGameRules()->BroadcastSound( 255, "MVM.Warning" );
|
||||
|
||||
switch( m_upgradeLevel )
|
||||
{
|
||||
//---------------------------------------
|
||||
case 1:
|
||||
m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_2nd_upgrade.GetFloat() );
|
||||
|
||||
// permanent buff banner effect (handled above)
|
||||
|
||||
// update the objective resource so clients have the information
|
||||
if ( TFObjectiveResource() )
|
||||
{
|
||||
TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 1 );
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() );
|
||||
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE1, TF_TEAM_PVE_DEFENDERS );
|
||||
DispatchParticleEffect( "mvm_levelup1", PATTACH_POINT_FOLLOW, me, "head" );
|
||||
}
|
||||
return true;
|
||||
|
||||
//---------------------------------------
|
||||
case 2:
|
||||
{
|
||||
static CSchemaAttributeDefHandle pAttrDef_HealthRegen( "health regen" );
|
||||
|
||||
m_upgradeTimer.Start( tf_mvm_bot_flag_carrier_interval_to_3rd_upgrade.GetFloat() );
|
||||
|
||||
if ( !pAttrDef_HealthRegen )
|
||||
{
|
||||
Warning( "TFBotSpawner: Invalid attribute 'health regen'\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
CAttributeList *pAttrList = me->GetAttributeList();
|
||||
if ( pAttrList )
|
||||
{
|
||||
pAttrList->SetRuntimeAttributeValue( pAttrDef_HealthRegen, tf_mvm_bot_flag_carrier_health_regen.GetFloat() );
|
||||
}
|
||||
}
|
||||
|
||||
// update the objective resource so clients have the information
|
||||
if ( TFObjectiveResource() )
|
||||
{
|
||||
TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 2 );
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( gpGlobals->curtime );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( gpGlobals->curtime + m_upgradeTimer.GetRemainingTime() );
|
||||
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE2, TF_TEAM_PVE_DEFENDERS );
|
||||
DispatchParticleEffect( "mvm_levelup2", PATTACH_POINT_FOLLOW, me, "head" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
case 3:
|
||||
// add critz
|
||||
me->m_Shared.AddCond( TF_COND_CRITBOOSTED );
|
||||
|
||||
// update the objective resource so clients have the information
|
||||
if ( TFObjectiveResource() )
|
||||
{
|
||||
TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 3 );
|
||||
TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
|
||||
TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
|
||||
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_CARRIER_UPGRADE3, TF_TEAM_PVE_DEFENDERS );
|
||||
DispatchParticleEffect( "mvm_levelup3", PATTACH_POINT_FOLLOW, me, "head" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotDeliverFlag::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CCaptureFlag *flag = me->GetFlagToFetch();
|
||||
|
||||
if ( !flag )
|
||||
{
|
||||
return Done( "No flag" );
|
||||
}
|
||||
|
||||
CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() );
|
||||
if ( !carrier || !me->IsSelf( carrier ) )
|
||||
{
|
||||
return Done( "I'm no longer carrying the flag" );
|
||||
}
|
||||
|
||||
if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
// let the bomb carrier use it's buff banners/etc
|
||||
Action< CTFBot > *result = me->OpportunisticallyUseWeaponAbilities();
|
||||
if ( result )
|
||||
{
|
||||
return SuspendFor( result, "Opportunistically using buff item" );
|
||||
}
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
// deliver the flag
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CCaptureZone *zone = me->GetFlagCaptureZone();
|
||||
|
||||
if ( !zone )
|
||||
{
|
||||
return Done( "No flag capture zone exists!" );
|
||||
}
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, zone->WorldSpaceCenter(), cost );
|
||||
|
||||
float flOldTravelDistance = m_flTotalTravelDistance;
|
||||
|
||||
m_flTotalTravelDistance = NavAreaTravelDistance( me->GetLastKnownArea(), TheNavMesh->GetNavArea( zone->WorldSpaceCenter() ), cost );
|
||||
|
||||
if ( flOldTravelDistance != -1.0f && m_flTotalTravelDistance - flOldTravelDistance > 2000.0f )
|
||||
{
|
||||
TFGameRules()->BroadcastSound( 255, "Announcer.MVM_Bomb_Reset" );
|
||||
|
||||
// Look for players that helped with the reset and send an event
|
||||
CUtlVector<CTFPlayer *> playerVector;
|
||||
CollectPlayers( &playerVector, TF_TEAM_PVE_DEFENDERS );
|
||||
FOR_EACH_VEC( playerVector, i )
|
||||
{
|
||||
CTFPlayer *pPlayer = playerVector[i];
|
||||
if ( !pPlayer )
|
||||
continue;
|
||||
|
||||
if ( me->m_AchievementData.IsPusherInHistory( pPlayer, 3.f ) )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "mvm_bomb_reset_by_player" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "player", pPlayer->entindex() );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
|
||||
CTF_GameStats.Event_PlayerAwardBonusPoints( pPlayer, me, 100 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( UpgradeOverTime( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotTaunt, "Taunting for our new upgrade" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotDeliverFlag::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
me->ClearAttribute( CTFBot::SUPPRESS_FIRE );
|
||||
|
||||
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
me->m_Shared.ResetRageBuffs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotDeliverFlag::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
if ( tf_mvm_bot_allow_flag_carrier_to_fight.GetBool() )
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// are we in a hurry?
|
||||
QueryResultType CTFBotDeliverFlag::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// is it time to retreat?
|
||||
QueryResultType CTFBotDeliverFlag::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotDeliverFlag::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
|
||||
{
|
||||
if ( TFGameRules()->IsMannVsMachineMode() && other && FClassnameIs( other, "func_capturezone" ) )
|
||||
{
|
||||
return TrySuspendFor( new CTFBotMvMDeployBomb, RESULT_CRITICAL, "Delivering the bomb!" );
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotPushToCapturePoint::CTFBotPushToCapturePoint( Action< CTFBot > *nextAction )
|
||||
{
|
||||
m_nextAction = nextAction;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPushToCapturePoint::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// flag collection and delivery is handled by our parent behavior, ScenarioMonitor
|
||||
|
||||
CCaptureZone *zone = me->GetFlagCaptureZone();
|
||||
|
||||
if ( !zone )
|
||||
{
|
||||
if ( m_nextAction )
|
||||
{
|
||||
return ChangeTo( m_nextAction, "No flag capture zone exists!" );
|
||||
}
|
||||
|
||||
return Done( "No flag capture zone exists!" );
|
||||
}
|
||||
|
||||
Vector toZone = zone->WorldSpaceCenter() - me->GetAbsOrigin();
|
||||
if ( toZone.AsVector2D().IsLengthLessThan( 50.0f ) )
|
||||
{
|
||||
if ( m_nextAction )
|
||||
{
|
||||
return ChangeTo( m_nextAction, "At destination" );
|
||||
}
|
||||
|
||||
return Done( "At destination" );
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, zone->WorldSpaceCenter(), cost );
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPushToCapturePoint::OnNavAreaChanged( CTFBot *me, CNavArea *newArea, CNavArea *oldArea )
|
||||
{
|
||||
// does the area we are entering have a prerequisite?
|
||||
if ( newArea && newArea->HasPrerequisite( me ) )
|
||||
{
|
||||
const CUtlVector< CHandle< CFuncNavPrerequisite > > &prereqVector = newArea->GetPrerequisiteVector();
|
||||
|
||||
for( int i=0; i<prereqVector.Count(); ++i )
|
||||
{
|
||||
const CFuncNavPrerequisite *prereq = prereqVector[i];
|
||||
if ( prereq && prereq->IsEnabled() && const_cast< CFuncNavPrerequisite * >( prereq )->PassesTriggerFilters( me ) )
|
||||
{
|
||||
// this prerequisite applies to me
|
||||
if ( prereq->IsTask( CFuncNavPrerequisite::TASK_WAIT ) )
|
||||
{
|
||||
return TrySuspendFor( new CTFBotNavEntWait( prereq ), RESULT_IMPORTANT, "Prerequisite commands me to wait" );
|
||||
}
|
||||
else if ( prereq->IsTask( CFuncNavPrerequisite::TASK_MOVE_TO_ENTITY ) )
|
||||
{
|
||||
return TrySuspendFor( new CTFBotNavEntMoveTo( prereq ), RESULT_IMPORTANT, "Prerequisite commands me to move to an entity" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_deliver_flag.h
|
||||
// Take the flag we are holding to its destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#ifndef TF_BOT_DELIVER_FLAG_H
|
||||
#define TF_BOT_DELIVER_FLAG_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotDeliverFlag : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual void OnEnd( CTFBot *me, Action< CTFBot > *nextAction );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const;
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const;
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const;
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
|
||||
|
||||
virtual const char *GetName( void ) const { return "DeliverFlag"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
float m_flTotalTravelDistance;
|
||||
|
||||
bool UpgradeOverTime( CTFBot *me );
|
||||
CountdownTimer m_upgradeTimer;
|
||||
|
||||
#define DONT_UPGRADE -1
|
||||
int m_upgradeLevel;
|
||||
|
||||
CountdownTimer m_buffPulseTimer;
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotPushToCapturePoint : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotPushToCapturePoint( Action< CTFBot > *nextAction = NULL );
|
||||
virtual ~CTFBotPushToCapturePoint() { }
|
||||
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual EventDesiredResult< CTFBot > OnNavAreaChanged( CTFBot *me, CNavArea *newArea, CNavArea *oldArea );
|
||||
|
||||
virtual const char *GetName( void ) const { return "PushToCapturePoint"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
Action< CTFBot > *m_nextAction;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_DELIVER_FLAG_H
|
||||
@@ -0,0 +1,142 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_escort_flag_carrier.cpp
|
||||
// Escort the flag carrier to their destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h"
|
||||
|
||||
extern ConVar tf_bot_flag_escort_range;
|
||||
|
||||
ConVar tf_bot_flag_escort_give_up_range( "tf_bot_flag_escort_give_up_range", "1000", FCVAR_CHEAT );
|
||||
ConVar tf_bot_flag_escort_max_count( "tf_bot_flag_escort_max_count", "4", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Count the number of TFBots currently engaged in the "EscortFlagCarrier" behavior
|
||||
//
|
||||
int GetBotEscortCount( int team )
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
CUtlVector< CTFPlayer * > livePlayerVector;
|
||||
CollectPlayers( &livePlayerVector, team, COLLECT_ONLY_LIVING_PLAYERS );
|
||||
|
||||
int i;
|
||||
for( i=0; i<livePlayerVector.Count(); ++i )
|
||||
{
|
||||
CTFBot *bot = dynamic_cast< CTFBot * >( livePlayerVector[i] );
|
||||
if ( bot )
|
||||
{
|
||||
Behavior< CTFBot > *behavior = (Behavior< CTFBot > *)bot->GetIntentionInterface()->FirstContainedResponder();
|
||||
if ( behavior )
|
||||
{
|
||||
Action< CTFBot > *action = (Action< CTFBot > *)behavior->FirstContainedResponder();
|
||||
|
||||
while( action && action->GetActiveChildAction() )
|
||||
{
|
||||
action = action->GetActiveChildAction();
|
||||
}
|
||||
|
||||
if ( action && action->IsNamed( "EscortFlagCarrier" ) )
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEscortFlagCarrier::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotEscortFlagCarrier::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CCaptureFlag *flag = me->GetFlagToFetch();
|
||||
|
||||
if ( !flag )
|
||||
{
|
||||
return Done( "No flag" );
|
||||
}
|
||||
|
||||
CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() );
|
||||
if ( !carrier )
|
||||
{
|
||||
return Done( "Flag was dropped" );
|
||||
}
|
||||
else if ( me->IsSelf( carrier ) )
|
||||
{
|
||||
return Done( "I picked up the flag!" );
|
||||
}
|
||||
|
||||
// stay near the carrier
|
||||
if ( me->IsRangeGreaterThan( carrier, tf_bot_flag_escort_give_up_range.GetFloat() ) )
|
||||
{
|
||||
if ( me->SelectRandomReachableEnemy() )
|
||||
{
|
||||
// too far away - give up
|
||||
return ChangeTo( new CTFBotAttackFlagDefenders, "Too far from flag carrier - attack defenders!" );
|
||||
}
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
CTFWeaponBase *myWeapon = me->m_Shared.GetActiveTFWeapon();
|
||||
if ( myWeapon && myWeapon->IsMeleeWeapon() )
|
||||
{
|
||||
if ( me->IsRangeLessThan( carrier, tf_bot_flag_escort_range.GetFloat() ) && me->IsLineOfSightClear( carrier ) )
|
||||
{
|
||||
ActionResult< CTFBot > result = m_meleeAttackAction.Update( me, interval );
|
||||
|
||||
if ( result.IsContinue() )
|
||||
{
|
||||
// we have a melee target, and we're still reasonably close to the flag carrier
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->IsRangeGreaterThan( carrier, 0.5f * tf_bot_flag_escort_range.GetFloat() ) )
|
||||
{
|
||||
// move near carrier
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
if ( GetBotEscortCount( me->GetTeamNumber() ) > tf_bot_flag_escort_max_count.GetInt() )
|
||||
{
|
||||
if ( me->SelectRandomReachableEnemy() )
|
||||
{
|
||||
return Done( "Too many flag escorts - giving up" );
|
||||
}
|
||||
}
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, carrier, cost );
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_escort_flag_carrier.h
|
||||
// Escort the flag carrier to their destination
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#ifndef TF_BOT_ESCORT_FLAG_CARRIER_H
|
||||
#define TF_BOT_ESCORT_FLAG_CARRIER_H
|
||||
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "bot/behavior/tf_bot_melee_attack.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotEscortFlagCarrier : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "EscortFlagCarrier"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
CTFBotMeleeAttack m_meleeAttackAction;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_ESCORT_FLAG_CARRIER_H
|
||||
@@ -0,0 +1,128 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_fetch_flag.cpp
|
||||
// Go get the flag!
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_fetch_flag.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_escort_flag_carrier.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_attack_flag_defenders.h"
|
||||
#include "bot/behavior/scenario/capture_the_flag/tf_bot_deliver_flag.h"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotFetchFlag::CTFBotFetchFlag( bool isTemporary )
|
||||
{
|
||||
m_isTemporary = isTemporary;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotFetchFlag::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotFetchFlag::Update( CTFBot *me, float interval )
|
||||
{
|
||||
CCaptureFlag *flag = me->GetFlagToFetch();
|
||||
|
||||
if ( !flag )
|
||||
{
|
||||
if ( TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
return SuspendFor( new CTFBotAttackFlagDefenders, "Flag flag exists - Attacking the enemy flag defenders" );
|
||||
}
|
||||
|
||||
return Done( "No flag" );
|
||||
}
|
||||
|
||||
// uncloak so we can attack
|
||||
if ( me->m_Shared.IsStealthed() )
|
||||
{
|
||||
me->PressAltFireButton();
|
||||
}
|
||||
|
||||
if ( TFGameRules()->IsMannVsMachineMode() && flag->IsHome() )
|
||||
{
|
||||
if ( gpGlobals->curtime - me->GetSpawnTime() < 1.0f && me->GetTeamNumber() != TEAM_SPECTATOR )
|
||||
{
|
||||
// we just spawned - give us the flag
|
||||
flag->PickUp( me, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( m_isTemporary )
|
||||
{
|
||||
return Done( "Flag unreachable" );
|
||||
}
|
||||
|
||||
// flag is at home and we're out in the world - can't reach it
|
||||
return SuspendFor( new CTFBotAttackFlagDefenders, "Flag unreachable at home - Attacking the enemy flag defenders" );
|
||||
}
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat )
|
||||
{
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
CTFPlayer *carrier = ToTFPlayer( flag->GetOwnerEntity() );
|
||||
if ( carrier )
|
||||
{
|
||||
if ( m_isTemporary )
|
||||
{
|
||||
return Done( "Someone else picked up the flag" );
|
||||
}
|
||||
|
||||
// NOTE: if I've picked up the flag, the ScenarioMonitor will handle it
|
||||
return SuspendFor( new CTFBotAttackFlagDefenders, "Someone has the flag - attacking the enemy defenders" );
|
||||
}
|
||||
|
||||
// go pick up the flag
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
float maxPathLength = TFGameRules()->IsMannVsMachineMode() ? TFBOT_MVM_MAX_PATH_LENGTH : 0.0f;
|
||||
if ( m_path.Compute( me, flag->WorldSpaceCenter(), cost, maxPathLength ) == false )
|
||||
{
|
||||
if ( flag->IsDropped() )
|
||||
{
|
||||
// flag is unreachable - attack for awhile and hope someone else can dislodge it
|
||||
return SuspendFor( new CTFBotAttackFlagDefenders( RandomFloat( 5.0f, 10.0f ) ), "Flag unreachable - Attacking" );
|
||||
|
||||
// just give it to me
|
||||
// flag->PickUp( me, true );
|
||||
}
|
||||
}
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// are we in a hurry?
|
||||
QueryResultType CTFBotFetchFlag::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// is it time to retreat?
|
||||
QueryResultType CTFBotFetchFlag::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_fetch_flag.h
|
||||
// Go get the flag!
|
||||
// Michael Booth, May 2011
|
||||
|
||||
#ifndef TF_BOT_FETCH_FLAG_H
|
||||
#define TF_BOT_FETCH_FLAG_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotFetchFlag : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
#define TEMPORARY_FLAG_FETCH true
|
||||
CTFBotFetchFlag( bool isTemporary = false );
|
||||
virtual ~CTFBotFetchFlag() { }
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const;
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const;
|
||||
|
||||
virtual const char *GetName( void ) const { return "FetchFlag"; };
|
||||
|
||||
private:
|
||||
bool m_isTemporary;
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_FETCH_FLAG_H
|
||||
@@ -0,0 +1,240 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_creep_wave.cpp
|
||||
// Move in a "creep wave" to the next available control point to capture it
|
||||
// Michael Booth, August 2010
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_CREEP_MODE
|
||||
|
||||
#include "team.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "bot/tf_bot_manager.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/creep_wave/tf_bot_creep_wave.h"
|
||||
|
||||
ConVar tf_creep_aggro_range( "tf_creep_aggro_range", "250" );
|
||||
ConVar tf_creep_give_up_range( "tf_creep_give_up_range", "300" );
|
||||
|
||||
|
||||
CTFPlayer *FindNearestEnemy( CTFBot *me, float maxRange )
|
||||
{
|
||||
CBasePlayer *closest = NULL;
|
||||
float closeRangeSq = maxRange * maxRange;
|
||||
|
||||
for( int i=1; i<=gpGlobals->maxClients; ++i )
|
||||
{
|
||||
CBasePlayer *player = static_cast< CBasePlayer * >( UTIL_PlayerByIndex( i ) );
|
||||
|
||||
if ( !player )
|
||||
continue;
|
||||
|
||||
if ( FNullEnt( player->edict() ) )
|
||||
continue;
|
||||
|
||||
if ( me->IsFriend( player ) )
|
||||
continue;
|
||||
|
||||
if ( !player->IsAlive() )
|
||||
continue;
|
||||
|
||||
float rangeSq = me->GetRangeSquaredTo( player );
|
||||
if ( rangeSq < closeRangeSq )
|
||||
{
|
||||
if ( me->IsLineOfFireClear( player ) )
|
||||
{
|
||||
closeRangeSq = rangeSq;
|
||||
closest = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (CTFPlayer *)closest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCreepWave::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
me->StopLookingAroundForEnemies();
|
||||
m_stuckTimer.Invalidate();
|
||||
|
||||
me->GetPlayerClass()->SetCustomModel( "models/bots/bot_heavy.mdl", USE_CLASS_ANIMATIONS );
|
||||
me->UpdateModel();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCreepWave::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !me->IsAlive() && me->StateGet() != TF_STATE_DYING )
|
||||
{
|
||||
// remove dead creeps for now
|
||||
engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", me->GetUserID() ) );
|
||||
}
|
||||
|
||||
CBaseCombatWeapon *melee = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( melee )
|
||||
{
|
||||
me->Weapon_Switch( melee );
|
||||
}
|
||||
|
||||
CUtlVector< CTeamControlPoint * > captureVector;
|
||||
TFGameRules()->CollectCapturePoints( me, &captureVector );
|
||||
|
||||
if ( captureVector.Count() == 0 )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, captureVector[0]->WorldSpaceCenter(), cost );
|
||||
}
|
||||
|
||||
m_path.Update( me );
|
||||
|
||||
if ( m_stuckTimer.HasStarted() )
|
||||
{
|
||||
// juke and dodge to escape stuck situation
|
||||
switch( RandomInt( 0, 3 ) )
|
||||
{
|
||||
case 0: me->PressBackwardButton(); break;
|
||||
case 1: me->PressForwardButton(); break;
|
||||
case 2: me->PressLeftButton(); break;
|
||||
case 3: me->PressRightButton(); break;
|
||||
}
|
||||
}
|
||||
|
||||
CTFPlayer *enemy = FindNearestEnemy( me, tf_creep_aggro_range.GetFloat() );
|
||||
if ( enemy )
|
||||
{
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_INCOMING );
|
||||
return SuspendFor( new CTFBotCreepAttack( enemy ), "Attacking nearby enemy" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotCreepWave::OnKilled( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && me->IsEnemy( info.GetAttacker() ) )
|
||||
{
|
||||
TheTFBots().OnCreepKilled( ToTFPlayer( info.GetAttacker() ) );
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotCreepWave::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_stuckTimer.Start();
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotCreepWave::OnUnStuck( CTFBot *me )
|
||||
{
|
||||
m_stuckTimer.Invalidate();
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotCreepAttack::CTFBotCreepAttack( CTFPlayer *victim )
|
||||
{
|
||||
m_victim = victim;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCreepAttack::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCreepAttack::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !me->IsAlive() && me->StateGet() != TF_STATE_DYING )
|
||||
{
|
||||
// remove dead creeps for now
|
||||
engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", me->GetUserID() ) );
|
||||
}
|
||||
|
||||
if ( m_victim.Get() == NULL || !m_victim->IsAlive() )
|
||||
{
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_JEERS );
|
||||
return Done( "Killed victim" );
|
||||
}
|
||||
|
||||
if ( me->IsRangeGreaterThan( m_victim, tf_creep_give_up_range.GetFloat() ) ||
|
||||
!me->IsLineOfFireClear( m_victim ) )
|
||||
{
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_NEGATIVE );
|
||||
return Done( "Lost victim" );
|
||||
}
|
||||
|
||||
CTFPlayer *newVictim = FindNearestEnemy( me, tf_creep_aggro_range.GetFloat() );
|
||||
if ( newVictim )
|
||||
{
|
||||
float newRangeSq = me->GetRangeSquaredTo( newVictim );
|
||||
float victimRangeSq = me->GetRangeSquaredTo( m_victim );
|
||||
|
||||
if ( newRangeSq < victimRangeSq )
|
||||
{
|
||||
// switch to closer target
|
||||
m_victim = newVictim;
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
|
||||
}
|
||||
}
|
||||
|
||||
CBaseCombatWeapon *melee = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( melee )
|
||||
{
|
||||
me->Weapon_Switch( melee );
|
||||
}
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( m_victim, IBody::CRITICAL, 0.2f, NULL, "Looking at enemy" );
|
||||
|
||||
// swing weapon
|
||||
me->PressFireButton();
|
||||
|
||||
// beeline towards our victim
|
||||
const float combatRange = 40.0f;
|
||||
if ( me->IsRangeGreaterThan( m_victim, combatRange ) )
|
||||
{
|
||||
me->GetLocomotionInterface()->Approach( m_victim->GetAbsOrigin() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// juke and dodge to avoid interpenetration
|
||||
switch( RandomInt( 0, 3 ) )
|
||||
{
|
||||
case 0: me->PressBackwardButton(); break;
|
||||
case 1: me->PressForwardButton(); break;
|
||||
case 2: me->PressLeftButton(); break;
|
||||
case 3: me->PressRightButton(); break;
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // TF_CREEP_MODE
|
||||
@@ -0,0 +1,57 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_creep_wave.h
|
||||
// Move in a "creep wave" to the next available control point to capture it
|
||||
// Michael Booth, August 2010
|
||||
|
||||
#ifndef TF_BOT_CREEP_WAVE_H
|
||||
#define TF_BOT_CREEP_WAVE_H
|
||||
|
||||
#ifdef TF_CREEP_MODE
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
|
||||
CTFBot *FindNearestEnemyCreep( CTFBot *me );
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotCreepWave : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnKilled( CTFBot *me, const CTakeDamageInfo &info );
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnUnStuck( CTFBot *me );
|
||||
|
||||
virtual const char *GetName( void ) const { return "CreepWave"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
IntervalTimer m_stuckTimer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotCreepAttack : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotCreepAttack( CTFPlayer *victim );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual const char *GetName( void ) const { return "CreepAttack"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFPlayer > m_victim;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_CREEP_MODE
|
||||
|
||||
#endif // TF_BOT_CREEP_WAVE_H
|
||||
@@ -0,0 +1,151 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_block.cpp
|
||||
// Prevent the other team from moving the cart
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "team_train_watcher.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/payload/tf_bot_payload_block.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadBlock::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
m_path.Invalidate();
|
||||
|
||||
m_giveUpTimer.Start( RandomFloat( 3.0f, 5.0f ) );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadBlock::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
if ( m_giveUpTimer.IsElapsed() )
|
||||
{
|
||||
return Done( "Been blocking long enough" );
|
||||
}
|
||||
|
||||
// move toward the point, periodically repathing to account for changing situation
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadBlock::Update( repath )", "NextBot" );
|
||||
|
||||
CTeamTrainWatcher *trainWatcher = TFGameRules()->GetPayloadToBlock( me->GetTeamNumber() );
|
||||
if ( !trainWatcher )
|
||||
{
|
||||
return Done( "Train Watcher is missing" );
|
||||
}
|
||||
|
||||
CBaseEntity *cart = trainWatcher->GetTrainEntity();
|
||||
if ( !cart )
|
||||
{
|
||||
return Done( "Cart is missing" );
|
||||
}
|
||||
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, cart->WorldSpaceCenter(), cost );
|
||||
m_repathTimer.Start( RandomFloat( 0.2f, 0.4f ) );
|
||||
}
|
||||
|
||||
// move towards next capture point
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadBlock::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadBlock::OnResume", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnStuck( CTFBot *me )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadBlock::OnStuck", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
me->GetLocomotionInterface()->ClearStuckStatus();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadBlock::OnMoveToFailure", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnTerritoryContested( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryToSustain( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnTerritoryCaptured( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryToSustain( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadBlock::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryToSustain( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadBlock::ShouldRetreat( const INextBot *bot ) const
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadBlock::ShouldHurry( const INextBot *bot ) const
|
||||
{
|
||||
// hurry and block the cart - don't retreat, etc
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_block.h
|
||||
// Prevent the other team from moving the cart
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#ifndef TF_BOT_PAYLOAD_BLOCK_H
|
||||
#define TF_BOT_PAYLOAD_BLOCK_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotPayloadBlock : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
|
||||
virtual const char *GetName( void ) const { return "PayloadBlock"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
CountdownTimer m_giveUpTimer;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_PAYLOAD_BLOCK_H
|
||||
@@ -0,0 +1,283 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_guard.cpp
|
||||
// Guard the payload and keep the attackers from getting near it
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "team_train_watcher.h"
|
||||
#include "trigger_area_capture.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/payload/tf_bot_payload_guard.h"
|
||||
#include "bot/behavior/scenario/payload/tf_bot_payload_block.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_payload_guard_range( "tf_bot_payload_guard_range", "1000", FCVAR_CHEAT );
|
||||
ConVar tf_bot_debug_payload_guard_vantage_points( "tf_bot_debug_payload_guard_vantage_points", 0, FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadGuard::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
m_path.Invalidate();
|
||||
|
||||
m_vantagePoint = me->GetAbsOrigin();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadGuard::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
CTeamTrainWatcher *trainWatcher = TFGameRules()->GetPayloadToBlock( me->GetTeamNumber() );
|
||||
if ( !trainWatcher )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
CBaseEntity *cart = trainWatcher->GetTrainEntity();
|
||||
if ( !cart )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( !trainWatcher->IsDisabled() && trainWatcher->GetCapturerCount() > 0 )
|
||||
{
|
||||
// the cart is being pushed ahead - block it
|
||||
if ( !m_moveToBlockTimer.HasStarted() )
|
||||
{
|
||||
m_moveToBlockTimer.Start( RandomFloat( 0.5f, 3.0f ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_moveToBlockTimer.HasStarted() && m_moveToBlockTimer.IsElapsed() )
|
||||
{
|
||||
m_moveToBlockTimer.Invalidate();
|
||||
|
||||
if ( trainWatcher->GetCapturerCount() >= 0 )
|
||||
{
|
||||
// the cart is not yet blocked - move to block it!
|
||||
return SuspendFor( new CTFBotPayloadBlock, "Moving to block the cart's forward motion" );
|
||||
}
|
||||
}
|
||||
|
||||
bool isMovingToVantagePoint = ( me->GetAbsOrigin() - m_vantagePoint ).AsVector2D().IsLengthGreaterThan( 25.0f );
|
||||
|
||||
if ( isMovingToVantagePoint )
|
||||
{
|
||||
// en route, don't change the point
|
||||
m_vantagePointTimer.Start( RandomFloat( 3.0f, 15.0f ) );
|
||||
}
|
||||
|
||||
if ( !me->IsLineOfFireClear( cart ) )
|
||||
{
|
||||
// cart is no longer visible from this area, find another one
|
||||
m_vantagePointTimer.Invalidate();
|
||||
}
|
||||
|
||||
if ( m_vantagePointTimer.IsElapsed() )
|
||||
{
|
||||
// find a new vantage point
|
||||
m_vantagePoint = FindVantagePoint( me, cart );
|
||||
m_repathTimer.Invalidate();
|
||||
isMovingToVantagePoint = true;
|
||||
}
|
||||
|
||||
if ( isMovingToVantagePoint )
|
||||
{
|
||||
// update our path periodically
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, m_vantagePoint, cost );
|
||||
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||||
}
|
||||
|
||||
// move towards our vantage point
|
||||
m_path.Update( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
// at vantage point
|
||||
if ( CTFBotPrepareStickybombTrap::IsPossible( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotPrepareStickybombTrap, "Laying sticky bombs!" );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadGuard::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadGuard::OnResume", "NextBot" );
|
||||
|
||||
m_vantagePointTimer.Invalidate();
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnStuck( CTFBot *me )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadGuard::OnStuck", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
me->GetLocomotionInterface()->ClearStuckStatus();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadGuard::OnMoveToFailure", "NextBot" );
|
||||
|
||||
m_vantagePointTimer.Invalidate();
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Invoked when cart is being pushed
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnTerritoryContested( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnTerritoryCaptured( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// Invoked when cart hits a checkpoint
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadGuard::OnTerritoryLost( CTFBot *me, int territoryID )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadGuard::ShouldRetreat( const INextBot *bot ) const
|
||||
{
|
||||
CTFBot *me = ToTFBot( bot->GetEntity() );
|
||||
|
||||
CTeamTrainWatcher *trainWatcher = TFGameRules()->GetPayloadToBlock( me->GetTeamNumber() );
|
||||
if ( trainWatcher && trainWatcher->IsTrainNearCheckpoint() )
|
||||
{
|
||||
// don't retreat if the cart is almost at the next checkpoint
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadGuard::ShouldHurry( const INextBot *bot ) const
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CCollectPayloadGuardVantagePoints : public ISearchSurroundingAreasFunctor
|
||||
{
|
||||
public:
|
||||
CCollectPayloadGuardVantagePoints( CTFBot *me, CBaseEntity *cart )
|
||||
{
|
||||
m_me = me;
|
||||
m_cart = cart;
|
||||
}
|
||||
|
||||
virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar )
|
||||
{
|
||||
CTFNavArea *area = (CTFNavArea *)baseArea;
|
||||
|
||||
// TODO: only use areas that are at/farther along than the payload
|
||||
|
||||
trace_t trace;
|
||||
NextBotTraceFilterIgnoreActors filter( NULL, COLLISION_GROUP_NONE );
|
||||
|
||||
const int tryCount = 3;
|
||||
|
||||
for( int i=0; i<tryCount; ++i )
|
||||
{
|
||||
Vector spot = area->GetRandomPoint();
|
||||
Vector eyeSpot = Vector( spot.x, spot.y, spot.z + HumanEyeHeight );
|
||||
|
||||
UTIL_TraceLine( eyeSpot, m_cart->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, &filter, &trace );
|
||||
|
||||
if ( !trace.DidHit() || trace.m_pEnt == m_cart )
|
||||
{
|
||||
m_vantagePointVector.AddToTail( spot );
|
||||
|
||||
if ( tf_bot_debug_payload_guard_vantage_points.GetBool() )
|
||||
{
|
||||
NDebugOverlay::Cross3D( spot, 5.0f, 255, 0, 255, true, 120.0f );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CTFBot *m_me;
|
||||
CBaseEntity *m_cart;
|
||||
CUtlVector< Vector > m_vantagePointVector;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//
|
||||
// Find a tactically advantageous area where we can see the payload
|
||||
//
|
||||
Vector CTFBotPayloadGuard::FindVantagePoint( CTFBot *me, CBaseEntity *cart )
|
||||
{
|
||||
CTFNavArea *cartArea = (CTFNavArea *)TheNavMesh->GetNearestNavArea( cart );
|
||||
|
||||
CCollectPayloadGuardVantagePoints collect( me, cart );
|
||||
SearchSurroundingAreas( cartArea, collect, tf_bot_payload_guard_range.GetFloat() );
|
||||
|
||||
if ( collect.m_vantagePointVector.Count() == 0 )
|
||||
return cart->WorldSpaceCenter();
|
||||
|
||||
int which = RandomInt( 0, collect.m_vantagePointVector.Count()-1 );
|
||||
return collect.m_vantagePointVector[ which ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_guard.h
|
||||
// Guard the payload and keep the attackers from getting near it
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#ifndef TF_BOT_PAYLOAD_GUARD_H
|
||||
#define TF_BOT_PAYLOAD_GUARD_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotPayloadGuard : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryContested( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryCaptured( CTFBot *me, int territoryID );
|
||||
virtual EventDesiredResult< CTFBot > OnTerritoryLost( CTFBot *me, int territoryID );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
|
||||
virtual const char *GetName( void ) const { return "PayloadGuard"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
Vector m_vantagePoint;
|
||||
CountdownTimer m_vantagePointTimer;
|
||||
Vector FindVantagePoint( CTFBot *me, CBaseEntity *cart );
|
||||
|
||||
CountdownTimer m_moveToBlockTimer;
|
||||
|
||||
};
|
||||
|
||||
#endif // TF_BOT_PAYLOAD_GUARD_H
|
||||
@@ -0,0 +1,155 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_push.cpp
|
||||
// Push the cartTrigger to the goal
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#include "cbase.h"
|
||||
#include "nav_mesh.h"
|
||||
#include "tf_player.h"
|
||||
#include "tf_gamerules.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "team_train_watcher.h"
|
||||
#include "trigger_area_capture.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/payload/tf_bot_payload_push.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
ConVar tf_bot_cart_push_radius( "tf_bot_cart_push_radius", "60", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadPush::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
m_path.Invalidate();
|
||||
|
||||
m_hideAngle = 180.0f;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadPush::Update( CTFBot *me, float interval )
|
||||
{
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
// prepare to fight
|
||||
me->EquipBestWeaponForThreat( threat );
|
||||
}
|
||||
|
||||
if ( TFGameRules()->InSetup() )
|
||||
{
|
||||
// wait until the gates open, then path
|
||||
m_path.Invalidate();
|
||||
m_repathTimer.Start( RandomFloat( 1.0f, 2.0f ) );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
CTeamTrainWatcher *trainWatcher = TFGameRules()->GetPayloadToPush( me->GetTeamNumber() );
|
||||
if ( !trainWatcher )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
CBaseEntity *cart = trainWatcher->GetTrainEntity();
|
||||
if ( !cart )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
// move toward the point, periodically repathing to account for changing situation
|
||||
if ( m_repathTimer.IsElapsed() )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadPush::Update( repath )", "NextBot" );
|
||||
|
||||
Vector cartForward;
|
||||
cart->GetVectors( &cartForward, NULL, NULL );
|
||||
|
||||
// default push position is behind cart
|
||||
Vector pushPos = cart->WorldSpaceCenter() - cartForward * tf_bot_cart_push_radius.GetFloat();
|
||||
|
||||
// try to hide from enemies on other side of cart
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat )
|
||||
{
|
||||
Vector enemyToCart = cart->WorldSpaceCenter() - threat->GetLastKnownPosition();
|
||||
enemyToCart.z = 0.0f;
|
||||
enemyToCart.NormalizeInPlace();
|
||||
|
||||
pushPos = cart->WorldSpaceCenter() + tf_bot_cart_push_radius.GetFloat() * enemyToCart;
|
||||
}
|
||||
|
||||
CTFBotPathCost cost( me, DEFAULT_ROUTE );
|
||||
m_path.Compute( me, pushPos, cost );
|
||||
|
||||
m_repathTimer.Start( RandomFloat( 0.2f, 0.4f ) );
|
||||
}
|
||||
|
||||
// push the cartTrigger
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotPayloadPush::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadPush::OnResume", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadPush::OnStuck( CTFBot *me )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadPush::OnStuck", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
me->GetLocomotionInterface()->ClearStuckStatus();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadPush::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotPayloadPush::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
VPROF_BUDGET( "CTFBotPayloadPush::OnMoveToFailure", "NextBot" );
|
||||
|
||||
m_repathTimer.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadPush::ShouldRetreat( const INextBot *bot ) const
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotPayloadPush::ShouldHurry( const INextBot *bot ) const
|
||||
{
|
||||
return ANSWER_UNDEFINED;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_payload_push.h
|
||||
// Push the cart to the goal
|
||||
// Michael Booth, April 2010
|
||||
|
||||
#ifndef TF_BOT_PAYLOAD_PUSH_H
|
||||
#define TF_BOT_PAYLOAD_PUSH_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotPayloadPush : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
|
||||
virtual const char *GetName( void ) const { return "PayloadPush"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
float m_hideAngle;
|
||||
};
|
||||
|
||||
#endif // TF_BOT_PAYLOAD_PUSH_H
|
||||
@@ -0,0 +1,217 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_raid_companion.cpp
|
||||
// Teammate bots for Raid mode
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "team.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_companion.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
#include "bot/behavior/tf_bot_move_to_vantage_point.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/sniper/tf_bot_sniper_lurk.h"
|
||||
|
||||
#include "bot/map_entities/tf_bot_generator.h" // action point
|
||||
|
||||
ConVar tf_raid_companion_follow_range( "tf_raid_companion_follow_range", "150", FCVAR_CHEAT );
|
||||
ConVar tf_raid_companion_allow_bot_leader( "tf_raid_companion_allow_bot_leader", "0", FCVAR_CHEAT );
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCompanion::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFPlayer *CTFBotCompanion::GetLeader( void )
|
||||
{
|
||||
CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE );
|
||||
CTFPlayer *leader = NULL;
|
||||
float leaderSpeed = FLT_MAX;
|
||||
|
||||
for( int i=0; i<raidingTeam->GetNumPlayers(); ++i )
|
||||
{
|
||||
CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i);
|
||||
|
||||
if ( player->IsBot() && !tf_raid_companion_allow_bot_leader.GetBool() )
|
||||
continue;
|
||||
|
||||
/*
|
||||
if ( player->IsPlayerClass( TF_CLASS_ENGINEER ) ||
|
||||
player->IsPlayerClass( TF_CLASS_SNIPER ) ||
|
||||
player->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
continue;
|
||||
*/
|
||||
|
||||
if ( player->IsAlive() )
|
||||
{
|
||||
float speed = player->GetPlayerClass()->GetMaxSpeed();
|
||||
|
||||
if ( speed < leaderSpeed )
|
||||
{
|
||||
leader = player;
|
||||
leaderSpeed = speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return leader;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCompanion::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
const CKnownEntity *patient = me->GetVisionInterface()->GetClosestKnown( me->GetTeamNumber() );
|
||||
if ( patient )
|
||||
{
|
||||
return SuspendFor( new CTFBotMedicHeal );
|
||||
}
|
||||
}
|
||||
|
||||
CTFPlayer *leader = GetLeader();
|
||||
if ( !leader )
|
||||
return Continue();
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
|
||||
if ( me->IsSelf( leader ) )
|
||||
{
|
||||
const float engageRange = 500.0f;
|
||||
if ( threat && threat->IsVisibleRecently() && me->IsRangeLessThan( threat->GetEntity(), engageRange ) )
|
||||
{
|
||||
// stop pushing ahead and kill nearby threats
|
||||
return SuspendFor( new CTFBotAttack, "Attacking nearby threats" );
|
||||
}
|
||||
|
||||
// head toward next capture point
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( point )
|
||||
{
|
||||
m_path.Update( me, point, cost );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ( !threat || threat->GetTimeSinceLastSeen() > 3.0f ) && leader->GetTimeSinceLastInjury() < 1.0f )
|
||||
{
|
||||
// we don't see anything, but the leader is under attack - find a better vantage point
|
||||
const float nearRange = 1000.0f;
|
||||
return SuspendFor( new CTFBotMoveToVantagePoint( nearRange ), "Moving to where I can see the enemy" );
|
||||
}
|
||||
|
||||
if ( leader && me->IsDistanceBetweenGreaterThan( leader, tf_raid_companion_follow_range.GetFloat() ) )
|
||||
{
|
||||
m_path.Update( me, leader, cost );
|
||||
}
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotCompanion::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotGuardian::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotGuardian::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( me->GetActionPoint() )
|
||||
{
|
||||
const float atHomeRange = 35.0f; // 25.0f;
|
||||
const Vector &home = me->GetActionPoint()->GetAbsOrigin();
|
||||
|
||||
if ( me->IsRangeGreaterThan( home, atHomeRange ) )
|
||||
{
|
||||
if ( m_repathTimer.IsElapsed() && !m_path.IsValid() )
|
||||
{
|
||||
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||||
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Compute( me, home, cost );
|
||||
}
|
||||
|
||||
// move home
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
|
||||
// at home
|
||||
m_path.Invalidate();
|
||||
me->SetHomeArea( me->GetLastKnownArea() );
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotEngineerBuild );
|
||||
}
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_SNIPER ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSniperLurk );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotGuardian::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardian::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryContinue( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardian::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryContinue( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardian::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryContinue( RESULT_IMPORTANT );
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
@@ -0,0 +1,57 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_companion.h
|
||||
// Teammate bots for Raid mode
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifndef TF_BOT_COMPANION_H
|
||||
#define TF_BOT_COMPANION_H
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
//
|
||||
// Friendly teammate bots
|
||||
//
|
||||
class CTFBotCompanion : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual const char *GetName( void ) const { return "Companion"; };
|
||||
|
||||
private:
|
||||
ChasePath m_path;
|
||||
CTFPlayer *GetLeader( void );
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Friendly defenders of the base
|
||||
//
|
||||
class CTFBotGuardian : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual const char *GetName( void ) const { return "Guardian"; };
|
||||
|
||||
private:
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
};
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // TF_BOT_COMPANION_H
|
||||
@@ -0,0 +1,261 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_guard_area.cpp
|
||||
// Defend an area against intruders
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "tf_player.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "team_control_point_master.h"
|
||||
#include "econ_entity_creation.h"
|
||||
#include "bot/behavior/tf_bot_retreat_to_cover.h"
|
||||
#include "bot/behavior/sniper/tf_bot_sniper_attack.h"
|
||||
#include "bot/behavior/engineer/tf_bot_engineer_build.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_wander.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_guard_area.h"
|
||||
#include "bot/behavior/tf_bot_attack.h"
|
||||
#include "bot/behavior/demoman/tf_bot_prepare_stickybomb_trap.h"
|
||||
|
||||
#include "nav_mesh.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
ConVar tf_bot_guard_aggro_range( "tf_bot_guard_aggro_range", "750", FCVAR_CHEAT );
|
||||
//ConVar tf_bot_guard_give_up_range( "tf_bot_guard_give_up_range", "1250", FCVAR_CHEAT );
|
||||
|
||||
ConVar tf_raid_special_vocalize_min_interval( "tf_raid_special_vocalize_min_interval", "10", FCVAR_CHEAT );
|
||||
ConVar tf_raid_special_vocalize_max_interval( "tf_raid_special_vocalize_max_interval", "15", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotGuardArea::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_chasePath.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
|
||||
/*
|
||||
// give this guy a hat!
|
||||
randomitemcriteria_t criteria;
|
||||
criteria.iItemLevel = AE_USE_SCRIPT_VALUE;
|
||||
criteria.iItemQuality = AE_USE_SCRIPT_VALUE;
|
||||
criteria.vecAbsOrigin = me->GetAbsOrigin();
|
||||
criteria.vecAbsAngles = vec3_angle;
|
||||
|
||||
switch( me->GetPlayerClass()->GetClassIndex() )
|
||||
{
|
||||
case TF_CLASS_SCOUT: criteria.pszItemName = "Scout Hat 1"; break;
|
||||
case TF_CLASS_SNIPER: criteria.pszItemName = "Sniper Hat 1"; break;
|
||||
case TF_CLASS_SOLDIER: criteria.pszItemName = "Soldier Pot Hat"; break;
|
||||
case TF_CLASS_DEMOMAN: criteria.pszItemName = "Demo Top Hat"; break;
|
||||
case TF_CLASS_MEDIC: criteria.pszItemName = "Medic Hat 1"; break;
|
||||
case TF_CLASS_HEAVYWEAPONS: criteria.pszItemName = "Heavy Ushanka Hat"; break;
|
||||
case TF_CLASS_PYRO: criteria.pszItemName = "Pyro Chicken Hat"; break;
|
||||
case TF_CLASS_SPY: criteria.pszItemName = "Spy Derby Hat"; break;
|
||||
case TF_CLASS_ENGINEER: criteria.pszItemName = "Engineer Hat 1"; break;
|
||||
default: criteria.pszItemName = ""; break;
|
||||
}
|
||||
|
||||
CBaseEntity *hat = ItemGeneration()->GenerateRandomItem( &criteria );
|
||||
if ( hat )
|
||||
{
|
||||
// Fake global id
|
||||
static int s_nFakeID = 1;
|
||||
static_cast< CEconEntity * >( hat )->GetAttributeContainer()->GetItem()->SetItemID( s_nFakeID++ );
|
||||
|
||||
DispatchSpawn( hat );
|
||||
static_cast< CEconEntity * >( hat )->GetAttributeContainer()->GetItem()->GenerateAttributes();
|
||||
static_cast< CEconEntity * >( hat )->GiveTo( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
Msg( "Failed to create hat\n" );
|
||||
}
|
||||
*/
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
class CFindVantagePoint : public ISearchSurroundingAreasFunctor
|
||||
{
|
||||
public:
|
||||
CFindVantagePoint( void )
|
||||
{
|
||||
m_vantageArea = NULL;
|
||||
}
|
||||
|
||||
virtual bool operator() ( CNavArea *baseArea, CNavArea *priorArea, float travelDistanceSoFar )
|
||||
{
|
||||
if ( travelDistanceSoFar > 2000.0f )
|
||||
return false;
|
||||
|
||||
CTFNavArea *area = (CTFNavArea *)baseArea;
|
||||
|
||||
CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE );
|
||||
for( int i=0; i<raidingTeam->GetNumPlayers(); ++i )
|
||||
{
|
||||
CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i);
|
||||
|
||||
if ( !player->IsAlive() || !player->GetLastKnownArea() )
|
||||
continue;
|
||||
|
||||
CTFNavArea *playerArea = (CTFNavArea *)player->GetLastKnownArea();
|
||||
if ( playerArea->IsCompletelyVisible( area ) )
|
||||
{
|
||||
// nearby area from which we can see the enemy team
|
||||
m_vantageArea = area;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CTFNavArea *m_vantageArea;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotGuardArea::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// emit vocalizations to warn players we're in the area
|
||||
if ( m_vocalizeTimer.IsElapsed() )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_special_vocalize_min_interval.GetFloat(), tf_raid_special_vocalize_max_interval.GetFloat() ) );
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_JEERS );
|
||||
}
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotMedicHeal );
|
||||
}
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_ENGINEER ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotEngineerBuild );
|
||||
}
|
||||
|
||||
const CKnownEntity *threat = me->GetVisionInterface()->GetPrimaryKnownThreat();
|
||||
if ( threat && threat->IsVisibleRecently() )
|
||||
{
|
||||
m_pathToVantageArea.Invalidate();
|
||||
|
||||
CTFNavArea *myArea = (CTFNavArea *)me->GetLastKnownArea();
|
||||
CTFNavArea *threatArea = (CTFNavArea *)threat->GetLastKnownArea();
|
||||
if ( myArea && threatArea )
|
||||
{
|
||||
if ( threatArea->GetIncursionDistance( TF_TEAM_BLUE ) < myArea->GetIncursionDistance( TF_TEAM_BLUE ) )
|
||||
{
|
||||
if ( me->IsRangeGreaterThan( threat->GetLastKnownPosition(), tf_bot_guard_aggro_range.GetFloat() ) )
|
||||
{
|
||||
// threat is far off and hasn't reached us yet - hide until they are closer
|
||||
return SuspendFor( new CTFBotRetreatToCover, "Hiding until threat gets closer" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attack!
|
||||
return SuspendFor( new CTFBotAttack, "Attacking nearby threat" );
|
||||
}
|
||||
else
|
||||
{
|
||||
// no enemy is visible
|
||||
Vector moveTo = me->GetAbsOrigin();
|
||||
|
||||
// if point is being captured, move to it
|
||||
CTeamControlPoint *point = me->GetMyControlPoint();
|
||||
if ( point && point->LastContestedAt() > 0.0f && ( gpGlobals->curtime - point->LastContestedAt() ) < 5.0f )
|
||||
{
|
||||
// the point is, or was very recently, contested - defend it!
|
||||
moveTo = point->GetAbsOrigin();
|
||||
}
|
||||
else if ( me->GetHomeArea() )
|
||||
{
|
||||
// no enemy is visible - return to our home position
|
||||
moveTo = me->GetHomeArea()->GetCenter();
|
||||
}
|
||||
|
||||
if ( !m_pathToPoint.IsValid() || m_repathTimer.IsElapsed() )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_pathToPoint.Compute( me, moveTo, cost );
|
||||
m_repathTimer.Start( RandomFloat( 2.0f, 3.0f ) );
|
||||
}
|
||||
|
||||
if ( ( me->GetAbsOrigin() - moveTo ).IsLengthGreaterThan( 25.0f ) )
|
||||
{
|
||||
m_pathToPoint.Update( me );
|
||||
}
|
||||
|
||||
if ( me->GetHomeArea() == me->GetLastKnownArea() )
|
||||
{
|
||||
// at home
|
||||
if ( CTFBotPrepareStickybombTrap::IsPossible( me ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotPrepareStickybombTrap, "Laying sticky bombs!" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// no enemy is visible - move to where we can see them
|
||||
if ( !m_pathToVantageArea.IsValid() )
|
||||
{
|
||||
CTFNavArea *vantageArea = me->FindVantagePoint();
|
||||
if ( vantageArea )
|
||||
{
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_pathToVantageArea.Compute( me, vantageArea->GetCenter(), cost );
|
||||
}
|
||||
}
|
||||
|
||||
m_pathToVantageArea.Update( me );
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardArea::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_chasePath.Invalidate();
|
||||
m_pathToPoint.Invalidate();
|
||||
m_pathToVantageArea.Invalidate();
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardArea::OnMoveToSuccess( CTFBot *me, const Path *path )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardArea::OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotGuardArea::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotGuardArea::OnCommandApproach( CTFBot *me, const Vector &pos, float range )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
@@ -0,0 +1,39 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_guard_area.h
|
||||
// Defend an area against intruders
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#ifndef TF_BOT_GUARD_AREA_H
|
||||
#define TF_BOT_GUARD_AREA_H
|
||||
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
class CTFBotGuardArea : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToSuccess( CTFBot *me, const Path *path );
|
||||
virtual EventDesiredResult< CTFBot > OnMoveToFailure( CTFBot *me, const Path *path, MoveToFailureType reason );
|
||||
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnCommandApproach( CTFBot *me, const Vector &pos, float range );
|
||||
|
||||
virtual const char *GetName( void ) const { return "GuardArea"; };
|
||||
|
||||
private:
|
||||
ChasePath m_chasePath;
|
||||
PathFollower m_pathToPoint;
|
||||
PathFollower m_pathToVantageArea;
|
||||
CountdownTimer m_vocalizeTimer;
|
||||
CountdownTimer m_repathTimer;
|
||||
};
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // TF_BOT_GUARD_AREA_H
|
||||
@@ -0,0 +1,164 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mob_rush.cpp
|
||||
// A member of a rushing mob of melee attackers
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "team.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/tf_bot_taunt.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_mob_rush.h"
|
||||
|
||||
|
||||
ConVar tf_bot_taunt_range( "tf_bot_taunt_range", "100", FCVAR_CHEAT );
|
||||
ConVar tf_raid_mob_rush_vocalize_min_interval( "tf_raid_mob_rush_vocalize_min_interval", "5", FCVAR_CHEAT );
|
||||
ConVar tf_raid_mob_rush_vocalize_max_interval( "tf_raid_mob_rush_vocalize_max_interval", "8", FCVAR_CHEAT );
|
||||
ConVar tf_raid_mob_avoid_range( "tf_raid_mob_avoid_range", "100", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMobRush::CTFBotMobRush( CTFPlayer *victim, float reactionTime )
|
||||
{
|
||||
m_victim = victim;
|
||||
|
||||
// this isn't strictly correct - we shouldn't start the timer until OnStart
|
||||
m_reactionTimer.Start( reactionTime );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMobRush::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_mob_rush_vocalize_min_interval.GetFloat(), tf_raid_mob_rush_vocalize_max_interval.GetFloat() ) );
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMobRush::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// mobs use only their melee weapons
|
||||
CBaseCombatWeapon *meleeWeapon = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( meleeWeapon )
|
||||
{
|
||||
me->Weapon_Switch( meleeWeapon );
|
||||
}
|
||||
|
||||
|
||||
if ( m_victim == NULL )
|
||||
{
|
||||
return Done( "No victim" );
|
||||
}
|
||||
|
||||
me->GetBodyInterface()->AimHeadTowards( m_victim, IBody::CRITICAL, 1.0f, NULL, "Looking at our melee target" );
|
||||
|
||||
if ( m_reactionTimer.HasStarted() )
|
||||
{
|
||||
if ( m_reactionTimer.IsElapsed() )
|
||||
{
|
||||
// snap out of it!
|
||||
me->DoAnimationEvent( PLAYERANIMEVENT_VOICE_COMMAND_GESTURE, ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY );
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
|
||||
m_reactionTimer.Invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// wait for reaction time to elapse
|
||||
return Continue();
|
||||
}
|
||||
}
|
||||
|
||||
if ( me->IsPlayingGesture( ACT_MP_GESTURE_VC_FINGERPOINT_PRIMARY ) )
|
||||
{
|
||||
// wait for "wake up" anim to finish
|
||||
return Continue();
|
||||
}
|
||||
|
||||
// just keep swinging
|
||||
me->PressFireButton();
|
||||
|
||||
// chase them down
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
m_path.Update( me, m_victim, cost );
|
||||
|
||||
// avoid friends
|
||||
CTeam *team = GetGlobalTeam( TF_TEAM_RED );
|
||||
for( int t=0; t<team->GetNumPlayers(); ++t )
|
||||
{
|
||||
CTFPlayer *teamMember = (CTFPlayer *)team->GetPlayer(t);
|
||||
|
||||
if ( !teamMember->IsAlive() )
|
||||
continue;
|
||||
|
||||
Vector toBuddy = teamMember->GetAbsOrigin() - me->GetAbsOrigin();
|
||||
if ( toBuddy.IsLengthLessThan( tf_raid_mob_avoid_range.GetFloat() ) )
|
||||
{
|
||||
float range = toBuddy.NormalizeInPlace();
|
||||
|
||||
me->GetLocomotionInterface()->Approach( me->GetAbsOrigin() - 100.0f * toBuddy, 1.0f - ( range / tf_raid_mob_avoid_range.GetFloat() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !m_victim->IsAlive() && me->IsRangeLessThan( m_victim, tf_bot_taunt_range.GetFloat() ) )
|
||||
{
|
||||
// we got 'em!
|
||||
return ChangeTo( new CTFBotTaunt, "Taunt their corpse" );
|
||||
}
|
||||
|
||||
if ( m_vocalizeTimer.IsElapsed() )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_mob_rush_vocalize_min_interval.GetFloat(), tf_raid_mob_rush_vocalize_max_interval.GetFloat() ) );
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_SCOUT ) )
|
||||
me->EmitSound( "Scout.MobJabber" );
|
||||
else if ( me->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
|
||||
me->EmitSound( "Heavy.MobJabber" );
|
||||
else
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMobRush::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
|
||||
{
|
||||
return TryToSustain( RESULT_CRITICAL, "Ignoring contact" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMobRush::OnInjured( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
return TryToSustain( RESULT_CRITICAL, "Ignoring injury" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMobRush::OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info )
|
||||
{
|
||||
return TryToSustain( RESULT_CRITICAL, "Ignoring friend death" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMobRush::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryToSustain( RESULT_CRITICAL );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
QueryResultType CTFBotMobRush::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
@@ -0,0 +1,42 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mob_rush.h
|
||||
// A member of a rushing mob of melee attackers
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifndef TF_BOT_MOB_RUSH_H
|
||||
#define TF_BOT_MOB_RUSH_H
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotMobRush : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMobRush( CTFPlayer *victim, float reactionTime = 0.0f );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
|
||||
virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info );
|
||||
virtual EventDesiredResult< CTFBot > OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info );
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
|
||||
QueryResultType ShouldRetreat( const INextBot *me ) const;
|
||||
|
||||
virtual const char *GetName( void ) const { return "MobRush"; };
|
||||
|
||||
private:
|
||||
CHandle< CTFPlayer > m_victim;
|
||||
CountdownTimer m_reactionTimer;
|
||||
CountdownTimer m_tauntTimer;
|
||||
CountdownTimer m_vocalizeTimer;
|
||||
ChasePath m_path;
|
||||
};
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // TF_BOT_MOB_RUSH_H
|
||||
@@ -0,0 +1,150 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_squad_attack.cpp
|
||||
// Move and attack as a small, cohesive, group
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "team.h"
|
||||
#include "raid/tf_raid_logic.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_wander.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_squad_attack.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/tf_bot_move_to_vantage_point.h"
|
||||
|
||||
|
||||
ConVar tf_squad_radius( "tf_squad_radius", "200", FCVAR_CHEAT );
|
||||
ConVar tf_squad_debug( "tf_squad_debug", "0", FCVAR_CHEAT );
|
||||
ConVar tf_raid_squad_vocalize_min_interval( "tf_raid_squad_vocalize_min_interval", "5", FCVAR_CHEAT );
|
||||
ConVar tf_raid_squad_vocalize_max_interval( "tf_raid_squad_vocalize_max_interval", "8", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSquadAttack::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_squad_vocalize_min_interval.GetFloat(), tf_raid_squad_vocalize_max_interval.GetFloat() ) );
|
||||
m_victim = NULL;
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
// the leader is the slowest member of the squad
|
||||
CTFBot *CTFBotSquadAttack::GetSquadLeader( CTFBot *me ) const
|
||||
{
|
||||
CTFBot *leader = NULL;
|
||||
float leaderSpeed = FLT_MAX;
|
||||
|
||||
CTFBotSquad *squad = me->GetSquad();
|
||||
CTFBotSquad::Iterator it;
|
||||
for( it = squad->GetFirstMember(); it != squad->InvalidIterator(); it = squad->GetNextMember( it ) )
|
||||
{
|
||||
CTFBot *bot = it();
|
||||
|
||||
float speed = bot->GetPlayerClass()->GetMaxSpeed();
|
||||
|
||||
if ( speed < leaderSpeed )
|
||||
{
|
||||
leader = bot;
|
||||
leaderSpeed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
return leader;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSquadAttack::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( !me->IsInASquad() )
|
||||
return Done( "Not in a squad" );
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotMedicHeal );
|
||||
}
|
||||
|
||||
CTFBot *leader = GetSquadLeader( me );
|
||||
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||||
|
||||
if ( m_victim == NULL || m_victimConsiderTimer.IsElapsed() )
|
||||
{
|
||||
m_victimConsiderTimer.Start( 3.0f );
|
||||
|
||||
m_victim = TFGameRules()->GetRaidLogic()->SelectRaiderToAttack();
|
||||
}
|
||||
|
||||
if ( m_victim )
|
||||
{
|
||||
const float engageRange = 500.0f;
|
||||
if ( me->IsPlayerClass( TF_CLASS_PYRO ) ||
|
||||
me->IsRangeGreaterThan( m_victim->GetAbsOrigin(), engageRange ) ||
|
||||
!me->GetVisionInterface()->IsAbleToSee( m_victim, IVision::DISREGARD_FOV ) )
|
||||
{
|
||||
if ( me->IsSelf( leader ) || me->IsRangeLessThan( leader, tf_squad_radius.GetFloat() ) )
|
||||
{
|
||||
// chase down the enemy
|
||||
m_chasePath.Update( me, m_victim, cost );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !me->IsSelf( leader ) && me->IsRangeGreaterThan( leader, 1.25f * tf_squad_radius.GetFloat() ) )
|
||||
{
|
||||
// too far from leader - return to him
|
||||
m_chasePath.Update( me, leader, cost );
|
||||
}
|
||||
|
||||
if ( tf_squad_debug.GetBool() && me->IsSelf( leader ) )
|
||||
{
|
||||
NDebugOverlay::Circle( me->GetAbsOrigin(), 20.0f, 255, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
||||
|
||||
CTFBotSquad *squad = me->GetSquad();
|
||||
CTFBotSquad::Iterator it;
|
||||
for( it = squad->GetFirstMember(); it != squad->InvalidIterator(); it = squad->GetNextMember( it ) )
|
||||
{
|
||||
CTFBot *bot = it();
|
||||
|
||||
if ( me->IsSelf( bot ) )
|
||||
continue;
|
||||
|
||||
NDebugOverlay::Line( me->WorldSpaceCenter(), bot->WorldSpaceCenter(), 0, 255, 0, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_vocalizeTimer.IsElapsed() )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_squad_vocalize_min_interval.GetFloat(), tf_raid_squad_vocalize_max_interval.GetFloat() ) );
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_SCOUT ) )
|
||||
me->EmitSound( "Scout.MobJabber" );
|
||||
else if ( me->IsPlayerClass( TF_CLASS_HEAVYWEAPONS ) )
|
||||
me->EmitSound( "Heavy.MobJabber" );
|
||||
else
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_BATTLECRY );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotSquadAttack::OnResume( CTFBot *me, Action< CTFBot > *interruptingAction )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return Continue();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotSquadAttack::OnStuck( CTFBot *me )
|
||||
{
|
||||
m_path.Invalidate();
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
@@ -0,0 +1,47 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_squad_attack.h
|
||||
// Move and attack as a small, cohesive, group
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifndef TF_BOT_SQUAD_ATTACK_H
|
||||
#define TF_BOT_SQUAD_ATTACK_H
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
#include "Path/NextBotChasePath.h"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotSquadAttack : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual ActionResult< CTFBot > OnResume( CTFBot *me, Action< CTFBot > *interruptingAction );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
|
||||
QueryResultType ShouldRetreat( const INextBot *me ) const;
|
||||
|
||||
virtual const char *GetName( void ) const { return "SquadPatrol"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_vocalizeTimer;
|
||||
PathFollower m_path;
|
||||
ChasePath m_chasePath;
|
||||
CHandle< CTFPlayer > m_victim;
|
||||
CountdownTimer m_victimConsiderTimer;
|
||||
|
||||
CTFBot *GetSquadLeader( CTFBot *me ) const;
|
||||
};
|
||||
|
||||
inline QueryResultType CTFBotSquadAttack::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
|
||||
#endif // TF_BOT_SQUAD_ATTACK_H
|
||||
@@ -0,0 +1,175 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_wander.cpp
|
||||
// Wanderering/idle enemies for Squad Co-op mode
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#include "cbase.h"
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
#include "team.h"
|
||||
#include "raid/tf_raid_logic.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_wander.h"
|
||||
#include "bot/behavior/scenario/raid/tf_bot_mob_rush.h"
|
||||
|
||||
|
||||
ConVar tf_raid_wanderer_aggro_range( "tf_raid_wanderer_aggro_range", "500", FCVAR_CHEAT, "If wanderers see a threat closer than this, they attack" );
|
||||
ConVar tf_raid_wanderer_notice_friend_death_range( "tf_raid_wanderer_notice_friend_death_range", "1000", FCVAR_CHEAT, "If a friend dies within this radius of a wanderer, it wakes up and attacks the attacker" );
|
||||
ConVar tf_raid_wanderer_reaction_factor( "tf_raid_wanderer_reaction_factor", "1", FCVAR_CHEAT );
|
||||
ConVar tf_raid_wanderer_vocalize_min_interval( "tf_raid_wanderer_vocalize_min_interval", "20", FCVAR_CHEAT );
|
||||
ConVar tf_raid_wanderer_vocalize_max_interval( "tf_raid_wanderer_vocalize_max_interval", "30", FCVAR_CHEAT );
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotWander::CTFBotWander( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotWander::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_wanderer_vocalize_min_interval.GetFloat(), tf_raid_wanderer_vocalize_max_interval.GetFloat() ) );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotWander::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// mobs use only their melee weapons
|
||||
CBaseCombatWeapon *meleeWeapon = me->Weapon_GetSlot( TF_WPN_TYPE_MELEE );
|
||||
if ( meleeWeapon )
|
||||
{
|
||||
me->Weapon_Switch( meleeWeapon );
|
||||
}
|
||||
|
||||
|
||||
CTeam *raidingTeam = GetGlobalTeam( TF_TEAM_BLUE );
|
||||
|
||||
if ( me->HasAttribute( CTFBot::AGGRESSIVE ) )
|
||||
{
|
||||
// I'm a mob rusher - pick a random raider and attack them!
|
||||
CTFPlayer *victim = TFGameRules()->GetRaidLogic()->SelectRaiderToAttack();
|
||||
if ( victim )
|
||||
{
|
||||
return SuspendFor( new CTFBotMobRush( victim ), "Rushing a raider" );
|
||||
}
|
||||
}
|
||||
else if ( m_visionTimer.IsElapsed() )
|
||||
{
|
||||
// I'm a wanderer - look for very nearby threats
|
||||
m_visionTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||||
|
||||
// find closest visible raider within aggro range
|
||||
CTFPlayer *threat = NULL;
|
||||
float closeThreatRangeSq = tf_raid_wanderer_aggro_range.GetFloat() * tf_raid_wanderer_aggro_range.GetFloat();
|
||||
|
||||
for( int i=0; i<raidingTeam->GetNumPlayers(); ++i )
|
||||
{
|
||||
CTFPlayer *player = (CTFPlayer *)raidingTeam->GetPlayer(i);
|
||||
|
||||
if ( !player->IsAlive() )
|
||||
continue;
|
||||
|
||||
float rangeSq = me->GetRangeSquaredTo( player );
|
||||
if ( rangeSq < closeThreatRangeSq )
|
||||
{
|
||||
if ( me->GetVisionInterface()->IsLineOfSightClearToEntity( player ) )
|
||||
{
|
||||
threat = player;
|
||||
closeThreatRangeSq = rangeSq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( threat )
|
||||
{
|
||||
return SuspendFor( new CTFBotMobRush( threat ), "Attacking threat!" );
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_vocalizeTimer.IsElapsed() )
|
||||
{
|
||||
m_vocalizeTimer.Start( RandomFloat( tf_raid_wanderer_vocalize_min_interval.GetFloat(), tf_raid_wanderer_vocalize_max_interval.GetFloat() ) );
|
||||
|
||||
// mouth off
|
||||
if ( me->IsPlayerClass( TF_CLASS_SCOUT ) )
|
||||
me->EmitSound( "Scout.WanderJabber" );
|
||||
else
|
||||
me->SpeakConceptIfAllowed( MP_CONCEPT_PLAYER_JEERS );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotWander::OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result )
|
||||
{
|
||||
if ( other && other->IsPlayer() && me->IsEnemy( other ) )
|
||||
{
|
||||
return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)other ), RESULT_IMPORTANT, "Attacking threat who touched me!" );
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotWander::OnInjured( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && me->IsEnemy( info.GetAttacker() ) )
|
||||
{
|
||||
return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)info.GetAttacker() ), RESULT_IMPORTANT, "Attacking threat who attacked me!" );
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotWander::OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( victim && me->IsFriend( victim ) )
|
||||
{
|
||||
if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() && me->IsEnemy( info.GetAttacker() ) )
|
||||
{
|
||||
if ( me->IsRangeLessThan( victim, tf_raid_wanderer_notice_friend_death_range.GetFloat() ) )
|
||||
{
|
||||
if ( me->GetVisionInterface()->IsAbleToSee( victim, IVision::DISREGARD_FOV ) &&
|
||||
me->GetVisionInterface()->IsAbleToSee( info.GetAttacker(), IVision::DISREGARD_FOV ) )
|
||||
{
|
||||
float rangeToAttacker = me->GetRangeTo( info.GetAttacker() );
|
||||
float reactionTime;
|
||||
|
||||
if ( rangeToAttacker < tf_raid_wanderer_aggro_range.GetFloat() )
|
||||
{
|
||||
reactionTime = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
reactionTime = tf_raid_wanderer_reaction_factor.GetFloat() * ( rangeToAttacker - tf_raid_wanderer_aggro_range.GetFloat() ) / tf_raid_wanderer_aggro_range.GetFloat();
|
||||
}
|
||||
|
||||
return TrySuspendFor( new CTFBotMobRush( (CTFPlayer *)info.GetAttacker(), reactionTime ), RESULT_IMPORTANT, "Attacking my friend's attacker!" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotWander::OnCommandAttack( CTFBot *me, CBaseEntity *victim )
|
||||
{
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
#endif // TF_RAID_MODE
|
||||
@@ -0,0 +1,51 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_wander.h
|
||||
// Wanderering/idle enemies for Squad Co-op mode
|
||||
// Michael Booth, October 2009
|
||||
|
||||
#ifndef TF_BOT_WANDER_H
|
||||
#define TF_BOT_WANDER_H
|
||||
|
||||
#ifdef TF_RAID_MODE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotWander : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotWander( void );
|
||||
|
||||
virtual ActionResult< CTFBot > OnStart( CTFBot *me, Action< CTFBot > *priorAction );
|
||||
virtual ActionResult< CTFBot > Update( CTFBot *me, float interval );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnContact( CTFBot *me, CBaseEntity *other, CGameTrace *result = NULL );
|
||||
virtual EventDesiredResult< CTFBot > OnInjured( CTFBot *me, const CTakeDamageInfo &info );
|
||||
virtual EventDesiredResult< CTFBot > OnOtherKilled( CTFBot *me, CBaseCombatCharacter *victim, const CTakeDamageInfo &info );
|
||||
|
||||
virtual EventDesiredResult< CTFBot > OnCommandAttack( CTFBot *me, CBaseEntity *victim );
|
||||
|
||||
virtual QueryResultType ShouldHurry( const INextBot *me ) const; // are we in a hurry?
|
||||
virtual QueryResultType ShouldRetreat( const INextBot *me ) const; // is it time to retreat?
|
||||
|
||||
virtual const char *GetName( void ) const { return "Wander"; };
|
||||
|
||||
private:
|
||||
CountdownTimer m_visionTimer;
|
||||
CountdownTimer m_vocalizeTimer;
|
||||
};
|
||||
|
||||
|
||||
inline QueryResultType CTFBotWander::ShouldHurry( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_YES;
|
||||
}
|
||||
|
||||
|
||||
inline QueryResultType CTFBotWander::ShouldRetreat( const INextBot *me ) const
|
||||
{
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
#endif TF_RAID_MODE
|
||||
|
||||
#endif // TF_BOT_WANDER_H
|
||||
Reference in New Issue
Block a user