summaryrefslogtreecommitdiff
path: root/game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp')
-rw-r--r--game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp377
1 files changed, 377 insertions, 0 deletions
diff --git a/game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp b/game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp
new file mode 100644
index 0000000..07fe474
--- /dev/null
+++ b/game/server/tf/bot/behavior/missions/tf_bot_mission_reprogrammed.cpp
@@ -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
+