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.
378 lines
9.1 KiB
378 lines
9.1 KiB
4 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
// tf_bot_mission_reprogrammed.cpp
|
||
|
// Move to target and explode
|
||
|
// Michael Booth, October 2011
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "tf_team.h"
|
||
|
#include "nav_mesh.h"
|
||
|
#include "tf_player.h"
|
||
|
#include "bot/tf_bot.h"
|
||
|
#include "bot/behavior/missions/tf_bot_mission_reprogrammed.h"
|
||
|
#include "particle_parse.h"
|
||
|
#include "tf_obj_sentrygun.h"
|
||
|
|
||
|
#ifdef STAGING_ONLY
|
||
|
|
||
|
extern ConVar tf_bot_path_lookahead_range;
|
||
|
extern ConVar tf_bot_suicide_bomb_range;
|
||
|
|
||
|
ConVar tf_bot_reprogrammed_time( "tf_bot_reprogrammed_time", "8", FCVAR_CHEAT );
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
CTFBotMissionReprogrammed::CTFBotMissionReprogrammed( void )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
ActionResult< CTFBot > CTFBotMissionReprogrammed::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||
|
{
|
||
|
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||
|
m_detonateTimer.Invalidate();
|
||
|
m_detonateSeekTimer.Invalidate();
|
||
|
m_reprogrammedTimer.Invalidate();
|
||
|
m_hasDetonated = false;
|
||
|
m_consecutivePathFailures = 0;
|
||
|
m_wasSuccessful = false;
|
||
|
|
||
|
if ( me->HasTheFlag() )
|
||
|
me->DropFlag();
|
||
|
me->SetFlagTarget( NULL );
|
||
|
|
||
|
m_victim = me->GetMissionTarget();
|
||
|
|
||
|
// Find nearest, accessible teammate
|
||
|
if ( !m_victim )
|
||
|
{
|
||
|
CTFPlayer *pTarget = FindNearestEnemy( me );
|
||
|
if ( pTarget )
|
||
|
{
|
||
|
me->SetMissionTarget( pTarget );
|
||
|
m_victim = pTarget;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_victim )
|
||
|
{
|
||
|
m_lastKnownVictimPosition = m_victim->GetAbsOrigin();
|
||
|
}
|
||
|
|
||
|
// We're guaranteed to stay alive for a period of time
|
||
|
me->SetHealth( 1 );
|
||
|
me->m_takedamage = DAMAGE_NO;
|
||
|
|
||
|
// Duration
|
||
|
m_reprogrammedTimer.Start( tf_bot_reprogrammed_time.GetFloat() );
|
||
|
|
||
|
return Continue();
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
ActionResult< CTFBot > CTFBotMissionReprogrammed::Update( CTFBot *me, float interval )
|
||
|
{
|
||
|
bool bDetonate = false;
|
||
|
|
||
|
// one we start detonating, there's no turning back
|
||
|
if ( m_detonateTimer.HasStarted() )
|
||
|
{
|
||
|
if ( m_detonateTimer.IsElapsed() )
|
||
|
{
|
||
|
m_vecDetLocation = me->GetAbsOrigin();
|
||
|
Detonate( me );
|
||
|
|
||
|
return Done( "KABOOM!" );
|
||
|
}
|
||
|
|
||
|
return Continue();
|
||
|
}
|
||
|
|
||
|
// Find nearest, accessible teammate
|
||
|
if ( !m_victim || !m_victim->IsAlive() )
|
||
|
{
|
||
|
m_victim.Set( NULL );
|
||
|
|
||
|
CTFPlayer *pTarget = FindNearestEnemy( me );
|
||
|
if ( pTarget )
|
||
|
{
|
||
|
me->SetMissionTarget( pTarget );
|
||
|
m_victim = pTarget;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_victim )
|
||
|
{
|
||
|
me->m_Shared.RemoveCond( TF_COND_STUNNED );
|
||
|
|
||
|
// update chase destination
|
||
|
if ( m_victim->IsAlive() && !m_victim->IsEffectActive( EF_NODRAW ) )
|
||
|
{
|
||
|
m_lastKnownVictimPosition = m_victim->GetAbsOrigin();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_talkTimer.IsElapsed() )
|
||
|
{
|
||
|
m_talkTimer.Start( 4.0f );
|
||
|
me->EmitSound( "MVM.SentryBusterIntro" );
|
||
|
}
|
||
|
|
||
|
if ( m_repathTimer.IsElapsed() )
|
||
|
{
|
||
|
m_repathTimer.Start( RandomFloat( 0.5f, 1.0f ) );
|
||
|
|
||
|
CTFBotPathCost cost( me, FASTEST_ROUTE );
|
||
|
m_path.Compute( me, m_lastKnownVictimPosition, cost );
|
||
|
|
||
|
if ( m_path.Compute( me, m_lastKnownVictimPosition, cost ) == false )
|
||
|
{
|
||
|
++m_consecutivePathFailures;
|
||
|
|
||
|
if ( m_consecutivePathFailures >= 3 )
|
||
|
{
|
||
|
bDetonate = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// move to the victim
|
||
|
m_path.Update( me );
|
||
|
|
||
|
// Reprogramming time is up. Find nearest and detonate.
|
||
|
if ( m_reprogrammedTimer.IsElapsed() )
|
||
|
{
|
||
|
// Limit this mode to 5 seconds
|
||
|
if ( !m_detonateSeekTimer.HasStarted() )
|
||
|
{
|
||
|
m_detonateSeekTimer.Start( 5.f );
|
||
|
}
|
||
|
else if ( m_detonateSeekTimer.IsElapsed() )
|
||
|
{
|
||
|
bDetonate = true;
|
||
|
}
|
||
|
|
||
|
// Get to a third of the damage range before detonating
|
||
|
const float detonateRange = tf_bot_suicide_bomb_range.GetFloat() / 3.0f;
|
||
|
if ( me->IsDistanceBetweenLessThan( m_lastKnownVictimPosition, detonateRange ) )
|
||
|
{
|
||
|
if ( me->IsLineOfFireClear( m_lastKnownVictimPosition + Vector( 0, 0, StepHeight ) ) )
|
||
|
{
|
||
|
bDetonate = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( bDetonate )
|
||
|
{
|
||
|
StartDetonate( me, true );
|
||
|
}
|
||
|
|
||
|
return Continue();
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
void CTFBotMissionReprogrammed::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||
|
{
|
||
|
if ( me->IsAlive() )
|
||
|
{
|
||
|
me->ForceChangeTeam( TEAM_SPECTATOR );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
EventDesiredResult< CTFBot > CTFBotMissionReprogrammed::OnKilled( CTFBot *me, const CTakeDamageInfo &info )
|
||
|
{
|
||
|
// Keep us alive, but run to nearest enemy
|
||
|
if ( !m_hasDetonated )
|
||
|
{
|
||
|
if ( !m_detonateTimer.HasStarted() )
|
||
|
{
|
||
|
StartDetonate( me );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Detonate( me );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TryContinue();
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
EventDesiredResult< CTFBot > CTFBotMissionReprogrammed::OnStuck( CTFBot *me )
|
||
|
{
|
||
|
// we're stuck, decide to detonate now!
|
||
|
if ( !m_hasDetonated && !m_detonateTimer.HasStarted() )
|
||
|
{
|
||
|
StartDetonate( me );
|
||
|
}
|
||
|
|
||
|
return TryContinue();
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
void CTFBotMissionReprogrammed::StartDetonate( CTFBot *me, bool wasSuccessful /* = false */ )
|
||
|
{
|
||
|
if ( m_detonateTimer.HasStarted() )
|
||
|
return;
|
||
|
|
||
|
if ( !me->IsAlive() || me->GetHealth() < 1 )
|
||
|
{
|
||
|
if ( me->GetTeamNumber() != TEAM_SPECTATOR)
|
||
|
{
|
||
|
me->m_lifeState = LIFE_ALIVE;
|
||
|
me->SetHealth( 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_wasSuccessful = wasSuccessful;
|
||
|
|
||
|
me->Taunt( TAUNT_BASE_WEAPON );
|
||
|
m_detonateTimer.Start( 2.f );
|
||
|
me->EmitSound( "MvM.SentryBusterSpin" );
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
void CTFBotMissionReprogrammed::Detonate( CTFBot *me )
|
||
|
{
|
||
|
// BLAST!
|
||
|
m_hasDetonated = true;
|
||
|
|
||
|
DispatchParticleEffect( "explosionTrail_seeds_mvm", me->GetAbsOrigin(), me->GetAbsAngles() );
|
||
|
DispatchParticleEffect( "fluidSmokeExpl_ring_mvm", me->GetAbsOrigin(), me->GetAbsAngles() );
|
||
|
|
||
|
me->EmitSound( "MVM.SentryBusterExplode" );
|
||
|
|
||
|
UTIL_ScreenShake( me->GetAbsOrigin(), 25.0f, 5.0f, 5.0f, 1000.0f, SHAKE_START );
|
||
|
|
||
|
if ( !m_wasSuccessful )
|
||
|
{
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_SENTRY_BUSTER_DOWN, TF_TEAM_PVE_DEFENDERS );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CUtlVector< CTFPlayer* > playerVector;
|
||
|
CUtlVector< CBaseCombatCharacter* > victimVector;
|
||
|
|
||
|
// Only damage our original team (reprogramming switches team)
|
||
|
CTFTeam *pTeam = me->GetOpposingTFTeam();
|
||
|
if ( pTeam )
|
||
|
{
|
||
|
CollectPlayers( &playerVector, pTeam->GetTeamNumber(), COLLECT_ONLY_LIVING_PLAYERS );
|
||
|
|
||
|
// objects
|
||
|
for ( int i = 0; i < pTeam->GetNumObjects(); ++i )
|
||
|
{
|
||
|
CBaseObject *object = pTeam->GetObject( i );
|
||
|
if ( object )
|
||
|
{
|
||
|
victimVector.AddToTail( object );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// players
|
||
|
for ( int i = 0; i < playerVector.Count(); ++i )
|
||
|
{
|
||
|
victimVector.AddToTail( playerVector[i] );
|
||
|
}
|
||
|
|
||
|
// non-player bots
|
||
|
CUtlVector< INextBot * > botVector;
|
||
|
TheNextBots().CollectAllBots( &botVector );
|
||
|
for( int i = 0; i < botVector.Count(); ++i )
|
||
|
{
|
||
|
CBaseCombatCharacter *bot = botVector[i]->GetEntity();
|
||
|
|
||
|
if ( !bot->IsPlayer() && bot->IsAlive() )
|
||
|
{
|
||
|
victimVector.AddToTail( bot );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Clear my mission before we have everyone take damage so I will die with the rest
|
||
|
me->SetMission( CTFBot::NO_MISSION, MISSION_DOESNT_RESET_BEHAVIOR_SYSTEM );
|
||
|
me->m_takedamage = DAMAGE_YES;
|
||
|
|
||
|
// kill victims (including me)
|
||
|
for( int i = 0; i < victimVector.Count(); ++i )
|
||
|
{
|
||
|
CBaseCombatCharacter *victim = victimVector[i];
|
||
|
|
||
|
Vector toVictim = victim->WorldSpaceCenter() - me->WorldSpaceCenter();
|
||
|
|
||
|
if ( toVictim.IsLengthGreaterThan( tf_bot_suicide_bomb_range.GetFloat() ) )
|
||
|
continue;
|
||
|
|
||
|
if ( victim->IsPlayer() )
|
||
|
{
|
||
|
color32 colorHit = { 255, 255, 255, 255 };
|
||
|
UTIL_ScreenFade( victim, colorHit, 1.0f, 0.1f, FFADE_IN );
|
||
|
}
|
||
|
|
||
|
if ( me->IsLineOfFireClear( victim ) )
|
||
|
{
|
||
|
toVictim.NormalizeInPlace();
|
||
|
|
||
|
const int nDamage = 1000;
|
||
|
CTakeDamageInfo info( me, me, nDamage, DMG_BLAST, TF_DMG_CUSTOM_NONE );
|
||
|
|
||
|
CalculateMeleeDamageForce( &info, toVictim, me->WorldSpaceCenter(), 1.0f );
|
||
|
victim->TakeDamage( info );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// make sure we're removed (in case we detonated in our spawn area where we are invulnerable)
|
||
|
me->ForceChangeTeam( TEAM_SPECTATOR );
|
||
|
}
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
CTFPlayer *CTFBotMissionReprogrammed::FindNearestEnemy( CTFBot *me )
|
||
|
{
|
||
|
CUtlVector< CTFPlayer* > playerVector;
|
||
|
|
||
|
CollectPlayers( &playerVector, GetEnemyTeam( me->GetTeamNumber() ), COLLECT_ONLY_LIVING_PLAYERS );
|
||
|
|
||
|
CTFPlayer *pClosestPlayer = NULL;
|
||
|
float flClosestPlayerDist = FLT_MAX;
|
||
|
|
||
|
FOR_EACH_VEC( playerVector, i )
|
||
|
{
|
||
|
if ( !playerVector[i] )
|
||
|
continue;
|
||
|
|
||
|
if ( playerVector[i] == me )
|
||
|
continue;
|
||
|
|
||
|
me->GetDistanceBetween( playerVector[i] );
|
||
|
|
||
|
// Find closest
|
||
|
float flDist = me->GetDistanceBetween( playerVector[i] );
|
||
|
if ( flDist < flClosestPlayerDist )
|
||
|
{
|
||
|
pClosestPlayer = playerVector[i];
|
||
|
flClosestPlayerDist = flDist;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return pClosestPlayer;
|
||
|
}
|
||
|
|
||
|
|
||
|
//---------------------------------------------------------------------------------------------
|
||
|
QueryResultType CTFBotMissionReprogrammed::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||
|
{
|
||
|
return ( m_detonateTimer.HasStarted() ) ? ANSWER_NO : ANSWER_YES;
|
||
|
}
|
||
|
|
||
|
#endif // STAGING_ONLY
|
||
|
|