mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 04:31:05 +00:00
1
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mission_destroy_sentries.cpp
|
||||
// Seek and destroy enemy sentries and ignore everything else
|
||||
// Michael Booth, June 2011
|
||||
|
||||
#include "cbase.h"
|
||||
#include "team.h"
|
||||
#include "bot/tf_bot.h"
|
||||
#include "bot/behavior/missions/tf_bot_mission_destroy_sentries.h"
|
||||
#include "bot/behavior/spy/tf_bot_spy_sap.h"
|
||||
#include "bot/behavior/tf_bot_destroy_enemy_sentry.h"
|
||||
#include "bot/behavior/medic/tf_bot_medic_heal.h"
|
||||
#include "bot/behavior/missions/tf_bot_mission_suicide_bomber.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
|
||||
//
|
||||
// NOTE: This behavior is deprecated and unused for now.
|
||||
// The only sentry destroying mission is the Sentry Buster right now (suicide bomber).
|
||||
//
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMissionDestroySentries::CTFBotMissionDestroySentries( CObjectSentrygun *goalSentry )
|
||||
{
|
||||
m_goalSentry = goalSentry;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CObjectSentrygun *CTFBotMissionDestroySentries::SelectSentryTarget( CTFBot *me )
|
||||
{
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMissionDestroySentries::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
if ( me->IsPlayerClass( TF_CLASS_MEDIC ) )
|
||||
{
|
||||
return ChangeTo( new CTFBotMedicHeal, "My job is to heal/uber the others in the mission" );
|
||||
}
|
||||
|
||||
// focus only on the mission
|
||||
me->SetAttribute( CTFBot::IGNORE_ENEMIES );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMissionDestroySentries::Update( CTFBot *me, float interval )
|
||||
{
|
||||
if ( m_goalSentry == NULL )
|
||||
{
|
||||
// first destroy the sentry we were assigned to, or any sentry we discovered or that is attacking us
|
||||
m_goalSentry = me->GetEnemySentry();
|
||||
|
||||
if ( m_goalSentry == NULL )
|
||||
{
|
||||
// next destroy the most dangerous sentry
|
||||
int iTeam = ( me->GetTeamNumber() == TF_TEAM_RED ) ? TF_TEAM_BLUE : TF_TEAM_RED;
|
||||
|
||||
if ( TFGameRules() && TFGameRules()->IsPVEModeActive() )
|
||||
{
|
||||
iTeam = TF_TEAM_PVE_DEFENDERS;
|
||||
}
|
||||
|
||||
m_goalSentry = TFGameRules()->FindSentryGunWithMostKills( iTeam );
|
||||
}
|
||||
}
|
||||
|
||||
// for suicide bombers, we never want them to revert to normal behavior even if there is no sentry to kill
|
||||
if ( me->IsPlayerClass( TF_CLASS_DEMOMAN ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotMissionSuicideBomber, "On a suicide mission to blow up a sentry" );
|
||||
}
|
||||
|
||||
if ( m_goalSentry == NULL )
|
||||
{
|
||||
// no sentries left to destroy - our mission is complete
|
||||
me->SetMission( CTFBot::NO_MISSION, MISSION_DOESNT_RESET_BEHAVIOR_SYSTEM );
|
||||
return ChangeTo( GetParentAction()->InitialContainedAction( me ), "Mission complete - reverting to normal behavior" );
|
||||
}
|
||||
|
||||
if ( m_goalSentry != me->GetEnemySentry() )
|
||||
{
|
||||
me->RememberEnemySentry( m_goalSentry, m_goalSentry->WorldSpaceCenter() );
|
||||
}
|
||||
|
||||
if ( me->IsPlayerClass( TF_CLASS_SPY ) )
|
||||
{
|
||||
return SuspendFor( new CTFBotSpySap( m_goalSentry ), "On a mission to sap a sentry" );
|
||||
}
|
||||
|
||||
return SuspendFor( new CTFBotDestroyEnemySentry, "On a mission to destroy a sentry" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotMissionDestroySentries::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
me->ClearAttribute( CTFBot::IGNORE_ENEMIES );
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mission_destroy_sentries.h
|
||||
// Seek and destroy enemy sentries and ignore everything else
|
||||
// Michael Booth, June 2011
|
||||
|
||||
#ifndef TF_BOT_MISSION_DESTROY_SENTRIES_H
|
||||
#define TF_BOT_MISSION_DESTROY_SENTRIES_H
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CTFBotMissionDestroySentries : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMissionDestroySentries( CObjectSentrygun *goalSentry = NULL );
|
||||
virtual ~CTFBotMissionDestroySentries() { }
|
||||
|
||||
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 const char *GetName( void ) const { return "MissionDestroySentries"; };
|
||||
|
||||
private:
|
||||
CHandle< CObjectSentrygun > m_goalSentry;
|
||||
|
||||
CObjectSentrygun *SelectSentryTarget( CTFBot *me );
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_MISSION_DESTROY_SENTRIES_H
|
||||
@@ -0,0 +1,377 @@
|
||||
//========= 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
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mission_suicide_bomber.h
|
||||
// Move to target and explode
|
||||
// Michael Booth, October 2011
|
||||
|
||||
#ifndef TF_BOT_MISSION_REPROGRAMMED_H
|
||||
#define TF_BOT_MISSION_REPROGRAMMED_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotMissionReprogrammed : public Action< CTFBot >
|
||||
{
|
||||
#ifdef STAGING_ONLY
|
||||
public:
|
||||
CTFBotMissionReprogrammed( void );
|
||||
|
||||
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 EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnKilled( CTFBot *me, const CTakeDamageInfo &info );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "MissionReprogrammed"; };
|
||||
|
||||
private:
|
||||
CHandle< CBaseEntity > m_victim; // the victim we are trying to destroy
|
||||
Vector m_lastKnownVictimPosition;
|
||||
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
CountdownTimer m_talkTimer;
|
||||
CountdownTimer m_detonateTimer;
|
||||
CountdownTimer m_detonateSeekTimer;
|
||||
CountdownTimer m_reprogrammedTimer;
|
||||
|
||||
void StartDetonate( CTFBot *me, bool wasSuccessful = false );
|
||||
void Detonate( CTFBot *me );
|
||||
CTFPlayer *FindNearestEnemy( CTFBot *me );
|
||||
bool m_hasDetonated;
|
||||
bool m_wasSuccessful;
|
||||
|
||||
int m_consecutivePathFailures;
|
||||
|
||||
Vector m_vecDetLocation;
|
||||
#endif // STAGING_ONLY
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_MISSION_REPROGRAMMED_H
|
||||
@@ -0,0 +1,400 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mission_suicide_bomber.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_suicide_bomber.h"
|
||||
#include "particle_parse.h"
|
||||
#include "tf_obj_sentrygun.h"
|
||||
#include "player_vs_environment/tf_populators.h"
|
||||
|
||||
extern ConVar tf_bot_path_lookahead_range;
|
||||
|
||||
ConVar tf_bot_suicide_bomb_range( "tf_bot_suicide_bomb_range", "300", FCVAR_CHEAT );
|
||||
ConVar tf_bot_suicide_bomb_friendly_fire( "tf_bot_suicide_bomb_friendly_fire", "1", FCVAR_CHEAT );
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
CTFBotMissionSuicideBomber::CTFBotMissionSuicideBomber( void )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMissionSuicideBomber::OnStart( CTFBot *me, Action< CTFBot > *priorAction )
|
||||
{
|
||||
m_path.SetMinLookAheadDistance( me->GetDesiredPathLookAheadRange() );
|
||||
m_detonateTimer.Invalidate();
|
||||
m_bHasDetonated = false;
|
||||
m_consecutivePathFailures = 0;
|
||||
m_bWasSuccessful = false;
|
||||
m_bWasKilled = false;
|
||||
|
||||
m_victim = me->GetMissionTarget();
|
||||
|
||||
if ( m_victim != NULL )
|
||||
{
|
||||
m_lastKnownVictimPosition = m_victim->GetAbsOrigin();
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
ActionResult< CTFBot > CTFBotMissionSuicideBomber::Update( CTFBot *me, float interval )
|
||||
{
|
||||
// one we start detonating, there's no turning back
|
||||
if ( m_detonateTimer.HasStarted() )
|
||||
{
|
||||
if ( m_detonateTimer.IsElapsed() )
|
||||
{
|
||||
m_vecDetLocation = me->GetAbsOrigin();
|
||||
Detonate( me );
|
||||
|
||||
// Send out an event
|
||||
if ( m_bWasSuccessful && m_victim && m_victim->IsBaseObject() )
|
||||
{
|
||||
CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( m_victim.Get() );
|
||||
if ( sentry && sentry->GetOwner() )
|
||||
{
|
||||
CTFPlayer *pOwner = ToTFPlayer( sentry->GetOwner() );
|
||||
if ( pOwner )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "mvm_sentrybuster_detonate" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "player", pOwner->entindex() );
|
||||
event->SetFloat( "det_x", m_vecDetLocation.x );
|
||||
event->SetFloat( "det_y", m_vecDetLocation.y );
|
||||
event->SetFloat( "det_z", m_vecDetLocation.z );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Done( "KABOOM!" );
|
||||
}
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
if ( me->GetHealth() == 1 )
|
||||
{
|
||||
// low on health - detonate where we are!
|
||||
StartDetonate( me, false, true );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
if ( m_victim != NULL )
|
||||
{
|
||||
// update chase destination
|
||||
if ( m_victim->IsAlive() && !m_victim->IsEffectActive( EF_NODRAW ) )
|
||||
{
|
||||
m_lastKnownVictimPosition = m_victim->GetAbsOrigin();
|
||||
}
|
||||
|
||||
// if the engineer is carrying his sentry, he becomes the victim
|
||||
if ( m_victim->IsBaseObject() )
|
||||
{
|
||||
CObjectSentrygun *sentry = dynamic_cast< CObjectSentrygun * >( m_victim.Get() );
|
||||
if ( sentry && sentry->IsCarried() && sentry->GetOwner() )
|
||||
{
|
||||
// path to the engineer carrying the sentry
|
||||
m_lastKnownVictimPosition = sentry->GetOwner()->GetAbsOrigin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ) ) )
|
||||
{
|
||||
StartDetonate( me, true );
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
if ( m_path.Compute( me, m_lastKnownVictimPosition, cost ) == false )
|
||||
{
|
||||
++m_consecutivePathFailures;
|
||||
|
||||
if ( m_consecutivePathFailures >= 3 )
|
||||
{
|
||||
// really can't reach my victim - detonate!
|
||||
StartDetonate( me );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_consecutivePathFailures = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// move to the victim
|
||||
m_path.Update( me );
|
||||
|
||||
return Continue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotMissionSuicideBomber::OnEnd( CTFBot *me, Action< CTFBot > *nextAction )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMissionSuicideBomber::OnKilled( CTFBot *me, const CTakeDamageInfo &info )
|
||||
{
|
||||
if ( !m_bHasDetonated )
|
||||
{
|
||||
if ( !m_detonateTimer.HasStarted() )
|
||||
{
|
||||
StartDetonate( me );
|
||||
}
|
||||
else if ( m_detonateTimer.IsElapsed() )
|
||||
{
|
||||
Detonate( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're in detonate mode, and something's trying to kill us. Prevent it.
|
||||
if ( me->GetTeamNumber() != TEAM_SPECTATOR )
|
||||
{
|
||||
me->m_lifeState = LIFE_ALIVE;
|
||||
me->SetHealth( 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
EventDesiredResult< CTFBot > CTFBotMissionSuicideBomber::OnStuck( CTFBot *me )
|
||||
{
|
||||
// we're stuck, decide to detonate now!
|
||||
if ( !m_bHasDetonated && !m_detonateTimer.HasStarted() )
|
||||
{
|
||||
StartDetonate( me );
|
||||
}
|
||||
|
||||
return TryContinue();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotMissionSuicideBomber::StartDetonate( CTFBot *me, bool bWasSuccessful /* = false */, bool bWasKilled /*= 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_bWasSuccessful = bWasSuccessful;
|
||||
m_bWasKilled = bWasKilled;
|
||||
|
||||
me->m_takedamage = DAMAGE_NO;
|
||||
|
||||
me->Taunt( TAUNT_BASE_WEAPON );
|
||||
m_detonateTimer.Start( 2.0f );
|
||||
me->EmitSound( "MvM.SentryBusterSpin" );
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
void CTFBotMissionSuicideBomber::Detonate( CTFBot *me )
|
||||
{
|
||||
// BLAST!
|
||||
m_bHasDetonated = 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_bWasSuccessful )
|
||||
{
|
||||
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||||
{
|
||||
TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_SENTRY_BUSTER_DOWN, TF_TEAM_PVE_DEFENDERS );
|
||||
|
||||
// ACHIEVEMENT_TF_MVM_KILL_SENTRY_BUSTER
|
||||
for ( int iDamager = 0 ; iDamager < MAX_ACHIEVEMENT_HISTORY_SLOTS ; iDamager ++ )
|
||||
{
|
||||
EntityHistory_t *damagerHistory = me->m_AchievementData.GetDamagerHistory( iDamager );
|
||||
if ( damagerHistory )
|
||||
{
|
||||
if ( damagerHistory->hEntity && ( gpGlobals->curtime - damagerHistory->flTimeDamage <= 5.0f ) )
|
||||
{
|
||||
CTFPlayer *pRecentDamager = ToTFPlayer( damagerHistory->hEntity );
|
||||
if ( pRecentDamager )
|
||||
{
|
||||
pRecentDamager->AwardAchievement( ACHIEVEMENT_TF_MVM_KILL_SENTRY_BUSTER );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CUtlVector< CTFPlayer * > playerVector;
|
||||
CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
|
||||
CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
|
||||
|
||||
CUtlVector< CBaseCombatCharacter * > victimVector;
|
||||
|
||||
int i;
|
||||
|
||||
// players
|
||||
for ( i=0; i<playerVector.Count(); ++i )
|
||||
{
|
||||
victimVector.AddToTail( playerVector[i] );
|
||||
}
|
||||
|
||||
// objects
|
||||
CTFTeam *team = GetGlobalTFTeam( TF_TEAM_BLUE );
|
||||
if ( team )
|
||||
{
|
||||
for ( i=0; i<team->GetNumObjects(); ++i )
|
||||
{
|
||||
CBaseObject *object = team->GetObject( i );
|
||||
if ( object )
|
||||
{
|
||||
victimVector.AddToTail( object );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
team = GetGlobalTFTeam( TF_TEAM_RED );
|
||||
if ( team )
|
||||
{
|
||||
for ( i=0; i<team->GetNumObjects(); ++i )
|
||||
{
|
||||
CBaseObject *object = team->GetObject( i );
|
||||
if ( object )
|
||||
{
|
||||
victimVector.AddToTail( object );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non-player bots
|
||||
CUtlVector< INextBot * > botVector;
|
||||
TheNextBots().CollectAllBots( &botVector );
|
||||
for( i=0; i<botVector.Count(); ++i )
|
||||
{
|
||||
CBaseCombatCharacter *bot = botVector[i]->GetEntity();
|
||||
|
||||
if ( !bot->IsPlayer() && bot->IsAlive() )
|
||||
{
|
||||
victimVector.AddToTail( bot );
|
||||
}
|
||||
}
|
||||
|
||||
// Send out an event whenever players damaged us to the point where we had to detonate
|
||||
if ( m_bWasKilled )
|
||||
{
|
||||
IGameEvent *event = gameeventmanager->CreateEvent( "mvm_sentrybuster_killed" );
|
||||
if ( event )
|
||||
{
|
||||
event->SetInt( "sentry_buster", me->entindex() );
|
||||
gameeventmanager->FireEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
int damage = MAX( victim->GetMaxHealth(), victim->GetHealth() );
|
||||
|
||||
CTakeDamageInfo info( me, me, 4 * damage, DMG_BLAST, TF_DMG_CUSTOM_NONE );
|
||||
if ( tf_bot_suicide_bomb_friendly_fire.GetBool() )
|
||||
{
|
||||
info.SetForceFriendlyFire( true );
|
||||
}
|
||||
|
||||
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->CommitSuicide( false, true );
|
||||
if ( me->IsAlive() )
|
||||
{
|
||||
me->ForceChangeTeam( TEAM_SPECTATOR );
|
||||
}
|
||||
|
||||
if ( m_bWasKilled )
|
||||
{
|
||||
// increment num sentry killed this wave
|
||||
CWave *pWave = g_pPopulationManager ? g_pPopulationManager->GetCurrentWave() : NULL;
|
||||
if ( pWave )
|
||||
{
|
||||
pWave->IncrementSentryBustersKilled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Should we attack "them"?
|
||||
QueryResultType CTFBotMissionSuicideBomber::ShouldAttack( const INextBot *me, const CKnownEntity *them ) const
|
||||
{
|
||||
// buster never "attacks", just approaches and self-detonates
|
||||
return ANSWER_NO;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||||
// tf_bot_mission_suicide_bomber.h
|
||||
// Move to target and explode
|
||||
// Michael Booth, October 2011
|
||||
|
||||
#ifndef TF_BOT_MISSION_SUICIDE_BOMBER_H
|
||||
#define TF_BOT_MISSION_SUICIDE_BOMBER_H
|
||||
|
||||
#include "Path/NextBotPathFollow.h"
|
||||
|
||||
class CTFBotMissionSuicideBomber : public Action< CTFBot >
|
||||
{
|
||||
public:
|
||||
CTFBotMissionSuicideBomber( void );
|
||||
|
||||
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 EventDesiredResult< CTFBot > OnStuck( CTFBot *me );
|
||||
virtual EventDesiredResult< CTFBot > OnKilled( CTFBot *me, const CTakeDamageInfo &info );
|
||||
|
||||
virtual QueryResultType ShouldAttack( const INextBot *me, const CKnownEntity *them ) const; // should we attack "them"?
|
||||
|
||||
virtual const char *GetName( void ) const { return "MissionSuicideBomber"; };
|
||||
|
||||
private:
|
||||
CHandle< CBaseEntity > m_victim; // the victim we are trying to destroy
|
||||
Vector m_lastKnownVictimPosition;
|
||||
|
||||
PathFollower m_path;
|
||||
CountdownTimer m_repathTimer;
|
||||
|
||||
CountdownTimer m_talkTimer;
|
||||
CountdownTimer m_detonateTimer;
|
||||
|
||||
void StartDetonate( CTFBot *me, bool bWasSuccessful = false, bool bWasKilled = false );
|
||||
void Detonate( CTFBot *me );
|
||||
bool m_bHasDetonated;
|
||||
bool m_bWasSuccessful;
|
||||
bool m_bWasKilled;
|
||||
|
||||
int m_consecutivePathFailures;
|
||||
|
||||
Vector m_vecDetLocation;
|
||||
};
|
||||
|
||||
|
||||
#endif // TF_BOT_MISSION_SUICIDE_BOMBER_H
|
||||
Reference in New Issue
Block a user