You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
7.9 KiB
262 lines
7.9 KiB
4 years ago
|
//========= 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
|