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.
240 lines
6.1 KiB
240 lines
6.1 KiB
4 years ago
|
//========= 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
|