summaryrefslogtreecommitdiff
path: root/game/server/tf/player_vs_environment/boss_alpha/behavior
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf/player_vs_environment/boss_alpha/behavior')
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.cpp109
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.h30
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.cpp170
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h41
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.cpp93
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h27
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.cpp129
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h28
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.cpp204
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h36
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.cpp119
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h38
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.cpp64
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h28
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.cpp208
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h31
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.cpp171
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h39
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.cpp81
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h32
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.cpp79
-rw-r--r--game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h25
22 files changed, 1782 insertions, 0 deletions
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.cpp
new file mode 100644
index 0000000..6cfe287
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.cpp
@@ -0,0 +1,109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_behavior.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "CRagdollMagnet.h"
+#include "tf_gamerules.h"
+#include "tf_shareddefs.h"
+#include "player_vs_environment/monster_resource.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h"
+
+
+//---------------------------------------------------------------------------------------------
+Action< CBossAlpha > *CBossAlphaBehavior::InitialContainedAction( CBossAlpha *me )
+{
+ return new CBossAlphaTacticalMonitor;
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaBehavior::Update( CBossAlpha *me, float interval )
+{
+ if ( m_vocalTimer.IsElapsed() )
+ {
+ m_vocalTimer.Start( RandomFloat( 3.0f, 5.0f ) );
+
+ if ( !me->IsBusy() )
+ {
+ me->EmitSound( "RobotBoss.Vocalize" );
+ }
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaBehavior::OnKilled( CBossAlpha *me, const CTakeDamageInfo &info )
+{
+ // relay the event to the map logic
+ me->m_outputOnKilled.FireOutput( me, me );
+
+ // Calculate death force
+ Vector forceVector = me->CalcDamageForceVector( info );
+
+ // See if there's a ragdoll magnet that should influence our force.
+ CRagdollMagnet *magnet = CRagdollMagnet::FindBestMagnet( me );
+ if ( magnet )
+ {
+ forceVector += magnet->GetForceVector( me );
+ }
+
+ if ( me->IsMiniBoss() )
+ {
+ me->EmitSound( "Cart.Explode" );
+ me->BecomeRagdoll( info, forceVector );
+
+ if ( g_pMonsterResource )
+ {
+ g_pMonsterResource->HideBossHealthMeter();
+ }
+ }
+ else
+ {
+ // full end-of-game boss
+ UTIL_Remove( me );
+
+ if ( TFGameRules()->IsBossBattleMode() )
+ {
+ // check that ALL bosses are dead
+ bool isBossBattleWon = true;
+
+ CBossAlpha *boss = NULL;
+ while( ( boss = (CBossAlpha *)gEntList.FindEntityByClassname( boss, "boss_alpha" ) ) != NULL )
+ {
+ if ( !me->IsSelf( boss ) && boss->IsAlive() && !boss->IsMiniBoss() )
+ {
+ isBossBattleWon = false;
+ }
+ }
+
+ if ( isBossBattleWon )
+ {
+ TFGameRules()->SetWinningTeam( TF_TEAM_BLUE, WINREASON_OPPONENTS_DEAD );
+
+ if ( g_pMonsterResource )
+ {
+ g_pMonsterResource->HideBossHealthMeter();
+ }
+ }
+ }
+ }
+
+ return TryDone();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaBehavior::OnContact( CBossAlpha *me, CBaseEntity *other, CGameTrace *result )
+{
+ return TryContinue();
+}
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.h
new file mode 100644
index 0000000..c637bf5
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_behavior.h
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_behavior.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_BEHAVIOR_H
+#define BOSS_ALPHA_BEHAVIOR_H
+
+#ifdef TF_RAID_MODE
+
+//---------------------------------------------------------------------------------------------
+class CBossAlphaBehavior : public Action< CBossAlpha >
+{
+public:
+ virtual Action< CBossAlpha > *InitialContainedAction( CBossAlpha *me );
+
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+
+ virtual EventDesiredResult< CBossAlpha > OnKilled( CBossAlpha *me, const CTakeDamageInfo &info );
+ virtual EventDesiredResult< CBossAlpha > OnContact( CBossAlpha *me, CBaseEntity *other, CGameTrace *result = NULL );
+
+ virtual const char *GetName( void ) const { return "Behavior"; } // return name of this action
+
+private:
+ CountdownTimer m_vocalTimer;
+};
+
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_BEHAVIOR_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.cpp
new file mode 100644
index 0000000..77204b7
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.cpp
@@ -0,0 +1,170 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_chase_victim.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h"
+
+extern ConVar tf_boss_alpha_grenade_launch_range;
+extern ConVar tf_boss_alpha_chase_range;
+
+
+//---------------------------------------------------------------------------------------------
+CBossAlphaChaseVictim::CBossAlphaChaseVictim( CBaseCombatCharacter *chaseTarget )
+{
+ m_chaseTarget = chaseTarget;
+ m_lastKnownTargetSpot = chaseTarget->GetAbsOrigin();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaChaseVictim::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ if ( m_chaseTarget == NULL )
+ {
+ return Done( "Target is NULL" );
+ }
+
+ m_lastKnownTargetSpot = m_chaseTarget->GetAbsOrigin();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaChaseVictim::Update( CBossAlpha *me, float interval )
+{
+ if ( m_chaseTarget == NULL || !m_chaseTarget->IsAlive() )
+ {
+ return ChangeTo( new CBossAlphaLostVictim, "No victim" );
+ }
+
+ if ( m_chaseTarget != me->GetAttackTarget() )
+ {
+ return Done( "Changing targets" );
+ }
+
+ Vector moveGoal = m_chaseTarget->GetAbsOrigin();
+
+ if ( me->IsLineOfSightClear( m_chaseTarget ) )
+ {
+ if ( !m_visibleTimer.HasStarted() )
+ {
+ m_visibleTimer.Start();
+ }
+
+ if ( me->HasAbility( CBossAlpha::CAN_NUKE ) && me->GetNukeTimer()->IsElapsed() )
+ {
+ return SuspendFor( new CBossAlphaNukeAttack, "Nuking!" );
+ }
+
+ m_lastKnownTargetSpot = m_chaseTarget->GetAbsOrigin();
+
+ if ( me->HasAbility( CBossAlpha::CAN_LAUNCH_STICKIES ) )
+ {
+ if ( ( me->GetGrenadeTimer()->IsElapsed() && me->IsRangeLessThan( m_chaseTarget, tf_boss_alpha_grenade_launch_range.GetFloat() ) ) ||
+ me->IsInCondition( CBossAlpha::ENRAGED ) )
+ {
+ return SuspendFor( new CBossAlphaLaunchGrenades, "Target is close (or I am enraged) - grenades!" );
+ }
+ }
+
+ // chase into line of sight a bit so they can't immediately get behind cover again
+ if ( me->HasAbility( CBossAlpha::CAN_FIRE_ROCKETS ) )
+ {
+ if ( m_visibleTimer.IsGreaterThen( 1.0f ) ||
+ me->IsRangeLessThan( m_chaseTarget, tf_boss_alpha_chase_range.GetFloat() ) )
+ {
+ return SuspendFor( new CBossAlphaLaunchRockets, "Fire!" );
+ }
+ }
+
+ if ( me->IsRangeLessThan( m_chaseTarget, 150.0f ) )
+ {
+ // too close - stand still
+ if ( !me->GetBodyInterface()->IsActivity( ACT_MP_STAND_MELEE ) )
+ {
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_MELEE );
+ }
+
+ return Continue();
+ }
+ }
+ else
+ {
+ m_visibleTimer.Invalidate();
+
+ // move to where we last saw our target
+ moveGoal = m_lastKnownTargetSpot;
+
+ if ( me->IsRangeLessThan( m_lastKnownTargetSpot, 20.0f ) )
+ {
+ // reached spot where we last saw our victim - give up
+ me->SetAttackTarget( NULL );
+
+ return ChangeTo( new CBossAlphaLostVictim, "I lost my chase victim" );
+ }
+ }
+
+
+ // move into sight of target
+ if ( m_path.GetAge() > 1.0f )
+ {
+ CBossAlphaPathCost cost( me );
+ m_path.Compute( me, moveGoal, cost );
+ }
+
+ me->GetLocomotionInterface()->Run();
+ m_path.Update( me );
+
+ // play running animation
+ if ( !me->GetBodyInterface()->IsActivity( ACT_MP_RUN_MELEE ) )
+ {
+ me->GetBodyInterface()->StartActivity( ACT_MP_RUN_MELEE );
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaChaseVictim::OnMoveToSuccess( CBossAlpha *me, const Path *path )
+{
+ return TryDone( RESULT_CRITICAL, "Reached move goal" );
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaChaseVictim::OnMoveToFailure( CBossAlpha *me, const Path *path, MoveToFailureType reason )
+{
+ return TryDone( RESULT_CRITICAL, "Path follow failed" );
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaChaseVictim::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaChaseVictim::OnStuck( CBossAlpha *me )
+{
+ // we're stuck - just warp to the our next path goal
+ if ( m_path.GetCurrentGoal() )
+ {
+ me->SetAbsOrigin( m_path.GetCurrentGoal()->pos + Vector( 0, 0, 10.0f ) );
+ }
+
+ return TryContinue();
+}
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h
new file mode 100644
index 0000000..4cdf5ff
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h
@@ -0,0 +1,41 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_chase_victim.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_CHASE_VICTIM_H
+#define BOSS_ALPHA_CHASE_VICTIM_H
+
+#ifdef TF_RAID_MODE
+
+#include "nav_mesh/tf_path_follower.h"
+
+//---------------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+class CBossAlphaChaseVictim : public Action< CBossAlpha >
+{
+public:
+ CBossAlphaChaseVictim( CBaseCombatCharacter *chaseTarget );
+
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ virtual EventDesiredResult< CBossAlpha > OnStuck( CBossAlpha *me );
+ virtual EventDesiredResult< CBossAlpha > OnMoveToSuccess( CBossAlpha *me, const Path *path );
+ virtual EventDesiredResult< CBossAlpha > OnMoveToFailure( CBossAlpha *me, const Path *path, MoveToFailureType reason );
+
+ virtual const char *GetName( void ) const { return "ChaseVictim"; } // return name of this action
+
+private:
+ CTFPathFollower m_path;
+ IntervalTimer m_visibleTimer;
+ CHandle< CBaseCombatCharacter > m_lastTarget;
+
+ CHandle< CBaseCombatCharacter > m_chaseTarget;
+ Vector m_lastKnownTargetSpot;
+};
+
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_CHASE_VICTIM_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.cpp
new file mode 100644
index 0000000..b68ca8e
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.cpp
@@ -0,0 +1,93 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_get_off_me.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_player.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h"
+
+ConVar tf_boss_alpha_charge_pushaway_force( "tf_boss_alpha_charge_pushaway_force", "500"/*, FCVAR_CHEAT*/ );
+
+
+//---------------------------------------------------------------------------------------------
+void PushawayPlayer( CTFPlayer *victim, const Vector &pushOrigin, float pushForce )
+{
+ if ( !victim )
+ return;
+
+ if ( victim->GetFlags() & FL_ONGROUND )
+ {
+ // launching into the air
+ victim->SetAbsVelocity( vec3_origin );
+
+ const float stunTime = 0.5f;
+ victim->m_Shared.StunPlayer( stunTime, 1.0, TF_STUN_MOVEMENT );
+
+ victim->ApplyPunchImpulseX( RandomInt( 10, 15 ) );
+ victim->SpeakConceptIfAllowed( MP_CONCEPT_DEFLECTED, "projectile:0,victim:1" );
+ }
+
+ victim->RemoveFlag( FL_ONGROUND );
+
+ Vector toVictim = victim->WorldSpaceCenter() - pushOrigin;
+ toVictim.z = 0.0f;
+ toVictim.NormalizeInPlace();
+ toVictim.z = 1.0f;
+
+ victim->ApplyAbsVelocityImpulse( pushForce * toVictim );
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaGetOffMe::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ me->AddGestureSequence( me->LookupSequence( "gesture_melee_help" ) );
+ m_timer.Start( 0.5f );
+
+ me->AddCondition( CBossAlpha::BUSY );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaGetOffMe::Update( CBossAlpha *me, float interval )
+{
+ if ( m_timer.IsElapsed() )
+ {
+ // blast players off of my head
+ CUtlVector< CTFPlayer * > onMeVector;
+ me->CollectPlayersStandingOnMe( &onMeVector );
+
+ Vector headPos;
+ QAngle headAngles;
+ if ( me->GetAttachment( "head", headPos, headAngles ) )
+ {
+ for( int i=0; i<onMeVector.Count(); ++i )
+ {
+ // push 'em off
+ PushawayPlayer( onMeVector[i], headPos, tf_boss_alpha_charge_pushaway_force.GetFloat() );
+ }
+ }
+
+ me->EmitSound( "Weapon_FlameThrower.AirBurstAttack" );
+
+ return Done();
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaGetOffMe::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->RemoveCondition( CBossAlpha::BUSY );
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h
new file mode 100644
index 0000000..5322103
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h
@@ -0,0 +1,27 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_get_off_me.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_GET_OFF_ME_H
+#define BOSS_ALPHA_GET_OFF_ME_H
+
+#ifdef TF_RAID_MODE
+
+
+//----------------------------------------------------------------------------
+class CBossAlphaGetOffMe : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ virtual const char *GetName( void ) const { return "GetOffMe"; } // return name of this action
+
+private:
+ CountdownTimer m_timer;
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_GET_OFF_ME_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.cpp
new file mode 100644
index 0000000..7f80e68
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.cpp
@@ -0,0 +1,129 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_guard_spot.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_player.h"
+#include "nav_mesh/tf_nav_area.h"
+#include "tf_projectile_rocket.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_chase_victim.h"
+
+
+//-----------------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaGuardSpot::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ m_path.SetMinLookAheadDistance( 300.0f );
+
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_ITEM1 );
+ me->SetHomePosition( me->GetAbsOrigin() );
+
+ m_lookAtSpot = vec3_origin;
+
+ return Continue();
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaGuardSpot::Update( CBossAlpha *me, float interval )
+{
+ CBaseCombatCharacter *target = me->GetAttackTarget();
+ if ( target )
+ {
+ if ( me->IsLineOfSightClear( target ) || me->IsPrisonerOfMinion( target ) )
+ {
+ return SuspendFor( new CBossAlphaChaseVictim( me->GetAttackTarget() ), "Get 'em!" );
+ }
+ }
+
+ CBaseCombatCharacter *visible = me->GetNearestVisibleEnemy();
+ if ( visible )
+ {
+ // look at visible victim out of range
+ me->GetLocomotionInterface()->FaceTowards( visible->WorldSpaceCenter() );
+ }
+
+ const float atHomeRange = 50.0f;
+ if ( me->IsRangeGreaterThan( me->GetHomePosition(), atHomeRange ) )
+ {
+ if ( m_path.GetAge() > 3.0f )
+ {
+ CBossAlphaPathCost cost( me );
+ if ( m_path.Compute( me, me->GetHomePosition(), cost ) == false )
+ {
+ // can't reach guard post - just jump there for now
+ me->Teleport( &me->GetHomePosition(), NULL, NULL );
+ }
+ }
+
+ m_path.Update( me );
+ }
+ else
+ {
+ // on guard spot - look around
+ if ( m_lookTimer.IsElapsed() )
+ {
+ m_lookTimer.Start( RandomFloat( 1.0f, 2.0f ) );
+
+ CTFNavArea *myArea = (CTFNavArea *)me->GetLastKnownArea();
+ if ( myArea )
+ {
+ const CUtlVector< CTFNavArea * > &invasionAreaVector = myArea->GetEnemyInvasionAreaVector( TF_TEAM_RED );
+
+ if ( invasionAreaVector.Count() > 0 )
+ {
+ // try to not look directly at walls
+ const float minGazeRange = 300.0f;
+ const int retryCount = 20.0f;
+ for( int r=0; r<retryCount; ++r )
+ {
+ int which = RandomInt( 0, invasionAreaVector.Count()-1 );
+ Vector gazeSpot = invasionAreaVector[ which ]->GetRandomPoint() + Vector( 0, 0, 0.75f * HumanHeight );
+
+ if ( me->IsRangeGreaterThan( gazeSpot, minGazeRange ) && me->GetVisionInterface()->IsLineOfSightClear( gazeSpot ) )
+ {
+ // use maxLookInterval so these looks override body aiming from path following
+ m_lookAtSpot = gazeSpot;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ me->GetLocomotionInterface()->FaceTowards( m_lookAtSpot );
+ }
+
+ if ( me->GetLocomotionInterface()->IsAttemptingToMove() )
+ {
+ // play running animation
+ if ( !me->GetBodyInterface()->IsActivity( ACT_MP_RUN_MELEE ) )
+ {
+ me->GetBodyInterface()->StartActivity( ACT_MP_RUN_MELEE );
+ }
+ }
+ else
+ {
+ // standing still
+ if ( !me->GetBodyInterface()->IsActivity( ACT_MP_STAND_ITEM1 ) )
+ {
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_ITEM1 );
+ }
+ }
+
+ return Continue();
+}
+
+
+//-----------------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaGuardSpot::OnInjured( CBossAlpha *me, const CTakeDamageInfo &info )
+{
+ return TryContinue();
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h
new file mode 100644
index 0000000..8fc8685
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h
@@ -0,0 +1,28 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_guard_spot.h
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "nav_mesh/tf_path_follower.h"
+
+//---------------------------------------------------------------------------------------------
+class CBossAlphaGuardSpot : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual EventDesiredResult< CBossAlpha > OnInjured( CBossAlpha *me, const CTakeDamageInfo &info );
+
+ virtual const char *GetName( void ) const { return "GuardSpot"; } // return name of this action
+
+private:
+ CTFPathFollower m_path;
+ CountdownTimer m_lookTimer;
+ Vector m_lookAtSpot;
+};
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.cpp
new file mode 100644
index 0000000..34008d3
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.cpp
@@ -0,0 +1,204 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_launch_grenades.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h"
+
+ConVar tf_boss_alpha_grenade_ring_min_horiz_vel( "tf_boss_alpha_grenade_ring_min_horiz_vel", "100"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_ring_max_horiz_vel( "tf_boss_alpha_grenade_ring_max_horiz_vel", "350"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_vert_vel( "tf_boss_alpha_grenade_vert_vel", "750"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_det_time( "tf_boss_alpha_grenade_det_time", "3"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_damage( "tf_boss_alpha_grenade_damage", "25"/*, FCVAR_CHEAT*/ );
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLaunchGrenades::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_SECONDARY );
+ m_animLayer = me->AddLayeredSequence( me->LookupSequence( "gesture_melee_cheer" ), 0 );
+
+ m_timer.Start( 1.0f );
+ m_detonateTimer.Invalidate();
+ me->AddCondition( CBossAlpha::BUSY );
+ me->GetGrenadeTimer()->Start( me->GetGrenadeInterval() );
+
+ me->EmitSound( "RobotBoss.LaunchGrenades" );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLaunchGrenades::LaunchGrenade( CBossAlpha *me, const Vector &launchVel, CTFWeaponInfo *weaponInfo )
+{
+ CTFGrenadePipebombProjectile *pProjectile = CTFGrenadePipebombProjectile::Create( me->WorldSpaceCenter() + Vector( 0, 0, 100 ), vec3_angle, launchVel,
+ AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ),
+ me, *weaponInfo, TF_PROJECTILE_PIPEBOMB_REMOTE, 1 );
+ if ( pProjectile )
+ {
+ pProjectile->SetLauncher( me );
+ pProjectile->SetDamage( tf_boss_alpha_grenade_damage.GetFloat() );
+
+ if ( me->IsInCondition( CBossAlpha::ENRAGED ) )
+ {
+ pProjectile->SetCritical( true );
+ }
+
+ m_grenadeVector.AddToTail( pProjectile );
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLaunchGrenades::LaunchGrenadeRings( CBossAlpha *me )
+{
+ const char *weaponAlias = WeaponIdToAlias( TF_WEAPON_GRENADELAUNCHER );
+ if ( !weaponAlias )
+ return;
+
+ WEAPON_FILE_INFO_HANDLE weaponInfoHandle = LookupWeaponInfoSlot( weaponAlias );
+ if ( weaponInfoHandle == GetInvalidWeaponInfoHandle() )
+ return;
+
+ CTFWeaponInfo *weaponInfo = static_cast< CTFWeaponInfo * >( GetFileWeaponInfoFromHandle( weaponInfoHandle ) );
+
+ QAngle myAngles = me->EyeAngles();
+
+ // create rings of stickies
+ float deltaVel = tf_boss_alpha_grenade_ring_max_horiz_vel.GetFloat() - tf_boss_alpha_grenade_ring_min_horiz_vel.GetFloat();
+ const int ringCount = 2;
+ for( int r=0; r<ringCount; ++r )
+ {
+ float u = (float)r/(float)(ringCount-1);
+
+ float horizVel = tf_boss_alpha_grenade_ring_min_horiz_vel.GetFloat() + u * deltaVel;
+
+ float angleDelta = 10.0f + 20.0f * ( 1.0f - u );
+
+ for( float angle=0.0f; angle<360.0f; angle += angleDelta )
+ {
+ Vector forward;
+ AngleVectors( myAngles, &forward );
+
+ Vector vecVelocity( horizVel * forward.x, horizVel * forward.y, tf_boss_alpha_grenade_vert_vel.GetFloat() );
+
+ LaunchGrenade( me, vecVelocity, weaponInfo );
+
+ myAngles.y += angleDelta;
+ }
+ }
+}
+
+
+ConVar tf_boss_alpha_grenade_spoke_angle( "tf_boss_alpha_grenade_spoke_angle", "45"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_spoke_count( "tf_boss_alpha_grenade_spoke_count", "15"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_spoke_min_horiz_vel( "tf_boss_alpha_grenade_spoke_min_horiz_vel", "100"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_grenade_spoke_max_horiz_vel( "tf_boss_alpha_grenade_spoke_max_horiz_vel", "750"/*, FCVAR_CHEAT*/ );
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLaunchGrenades::LaunchGrenadeSpokes( CBossAlpha *me )
+{
+ const char *weaponAlias = WeaponIdToAlias( TF_WEAPON_GRENADELAUNCHER );
+ if ( !weaponAlias )
+ return;
+
+ WEAPON_FILE_INFO_HANDLE weaponInfoHandle = LookupWeaponInfoSlot( weaponAlias );
+ if ( weaponInfoHandle == GetInvalidWeaponInfoHandle() )
+ return;
+
+ CTFWeaponInfo *weaponInfo = static_cast< CTFWeaponInfo * >( GetFileWeaponInfoFromHandle( weaponInfoHandle ) );
+
+ // create spokes of stickies
+ float deltaVel = tf_boss_alpha_grenade_spoke_max_horiz_vel.GetFloat() - tf_boss_alpha_grenade_spoke_min_horiz_vel.GetFloat();
+ float angleDelta = tf_boss_alpha_grenade_spoke_angle.GetFloat();
+ QAngle myAngles = me->EyeAngles();
+
+ for( float angle=0.0f; angle<360.0f; angle += angleDelta )
+ {
+ Vector forward;
+ AngleVectors( myAngles, &forward );
+
+ int spokeCount = tf_boss_alpha_grenade_spoke_count.GetInt();
+
+ for( int i=0; i<spokeCount; ++i )
+ {
+ float u = (float)i/(float)(spokeCount-1);
+
+ float horizVel = tf_boss_alpha_grenade_spoke_min_horiz_vel.GetFloat() + u * deltaVel;
+
+ Vector vecVelocity( horizVel * forward.x, horizVel * forward.y, tf_boss_alpha_grenade_vert_vel.GetFloat() );
+
+ LaunchGrenade( me, vecVelocity, weaponInfo );
+ }
+
+ myAngles.y += angleDelta;
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLaunchGrenades::Update( CBossAlpha *me, float interval )
+{
+ QAngle myAngles = me->EyeAngles();
+
+ if ( m_timer.HasStarted() && m_timer.IsElapsed() )
+ {
+ m_timer.Invalidate();
+
+ if ( RandomInt( 0, 100 ) < 50 )
+ {
+ LaunchGrenadeRings( me );
+ }
+ else
+ {
+ LaunchGrenadeSpokes( me );
+ }
+
+ me->EmitSound( "Weapon_Grenade_Normal.Single" );
+
+ m_detonateTimer.Start( tf_boss_alpha_grenade_det_time.GetFloat() );
+ }
+
+ if ( m_detonateTimer.HasStarted() && m_detonateTimer.IsElapsed() )
+ {
+ // detonate the stickies
+ for( int i=0; i<m_grenadeVector.Count(); ++i )
+ {
+ if ( m_grenadeVector[i] )
+ {
+ m_grenadeVector[i]->Detonate();
+ }
+ }
+
+ return Done();
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLaunchGrenades::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ // fizzle any outstanding stickies
+ for( int i=0; i<m_grenadeVector.Count(); ++i )
+ {
+ if ( m_grenadeVector[i] )
+ {
+ m_grenadeVector[i]->Fizzle();
+ m_grenadeVector[i]->Detonate();
+ }
+ }
+
+ me->RemoveCondition( CBossAlpha::ENRAGED );
+ me->RemoveCondition( CBossAlpha::BUSY );
+ me->FastRemoveLayer( m_animLayer );
+}
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h
new file mode 100644
index 0000000..592bfd5
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_grenades.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_launch_grenades.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_LAUNCH_GRENADES_H
+#define BOSS_ALPHA_LAUNCH_GRENADES_H
+
+#ifdef TF_RAID_MODE
+
+#include "tf_weapon_grenade_pipebomb.h"
+
+class CBossAlphaLaunchGrenades : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ // if anything interrupts this action, abort it
+ virtual ActionResult< CBossAlpha > OnSuspend( CBossAlpha *me, Action< CBossAlpha > *interruptingAction ) { return Done(); }
+
+ virtual const char *GetName( void ) const { return "LaunchGrenades"; } // return name of this action
+
+private:
+ CountdownTimer m_timer;
+ CountdownTimer m_detonateTimer;
+ CUtlVector< CHandle< CTFGrenadePipebombProjectile > > m_grenadeVector;
+ void LaunchGrenade( CBossAlpha *me, const Vector &launchVel, CTFWeaponInfo *weaponInfo );
+ void LaunchGrenadeRings( CBossAlpha *me );
+ void LaunchGrenadeSpokes( CBossAlpha *me );
+ int m_animLayer;
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_LAUNCH_GRENADES_H \ No newline at end of file
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.cpp
new file mode 100644
index 0000000..a1558d8
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.cpp
@@ -0,0 +1,119 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_launch_rockets.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_projectile_rocket.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h"
+
+ConVar tf_boss_alpha_dont_shoot( "tf_boss_alpha_dont_shoot", "0"/*, FCVAR_CHEAT*/ );
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLaunchRockets::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ // start animation
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_SECONDARY );
+
+ m_animLayer = me->AddLayeredSequence( me->LookupSequence( "taunt02" ), 0 );
+
+ m_timer.Start( 1.0f );
+
+ m_rocketsLeft = me->GetRocketLaunchCount();
+
+ me->AddCondition( CBossAlpha::BUSY );
+ me->LockAttackTarget();
+
+ me->EmitSound( "RobotBoss.LaunchRockets" );
+
+ if ( me->GetAttackTarget() == NULL )
+ {
+ return Done( "No target" );
+ }
+
+ m_target = me->GetAttackTarget();
+ m_lastTargetPosition = m_target->WorldSpaceCenter();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLaunchRockets::Update( CBossAlpha *me, float interval )
+{
+ if ( m_target != NULL )
+ {
+ m_lastTargetPosition = m_target->WorldSpaceCenter();
+ }
+
+ me->GetLocomotionInterface()->FaceTowards( m_lastTargetPosition );
+
+ if ( m_timer.IsElapsed() && m_launchTimer.IsElapsed() )
+ {
+ if ( !m_rocketsLeft )
+ {
+ return Done();
+ }
+
+ --m_rocketsLeft;
+ m_launchTimer.Start( me->GetRocketInterval() );
+
+ QAngle launchAngles = me->GetAbsAngles();
+
+ if ( m_target == NULL )
+ {
+ Vector to = m_lastTargetPosition - me->WorldSpaceCenter();
+ VectorAngles( to, launchAngles );
+ }
+ else
+ {
+ float range = me->GetRangeTo( m_target->EyePosition() );
+
+ const float rocketSpeed = me->GetRocketAimError() * 1100.0f; // 2000.0f; // 1100.0f; nerfing accuracy
+ float flightTime = range / rocketSpeed;
+
+ Vector aimSpot = m_target->EyePosition() + m_target->GetAbsVelocity() * flightTime;
+
+ Vector to = aimSpot - me->WorldSpaceCenter();
+ VectorAngles( to, launchAngles );
+ }
+
+ if ( !tf_boss_alpha_dont_shoot.GetBool() )
+ {
+ CTFProjectile_Rocket *pRocket = CTFProjectile_Rocket::Create( me, me->WorldSpaceCenter(), launchAngles, me, me );
+ if ( pRocket )
+ {
+ if ( me->IsInCondition( CBossAlpha::ENRAGED ) )
+ {
+ pRocket->SetCritical( true );
+ pRocket->EmitSound( "Weapon_RPG.SingleCrit" );
+ }
+ else
+ {
+ me->EmitSound( me->GetRocketSoundEffect() );
+ }
+
+ pRocket->SetDamage( me->GetRocketDamage() );
+ }
+ }
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLaunchRockets::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->RemoveCondition( CBossAlpha::ENRAGED );
+ me->RemoveCondition( CBossAlpha::BUSY );
+ me->FastRemoveLayer( m_animLayer );
+ me->UnlockAttackTarget();
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h
new file mode 100644
index 0000000..2f216b9
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_launch_rockets.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_launch_rockets.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_LAUNCH_ROCKETS_H
+#define BOSS_ALPHA_LAUNCH_ROCKETS_H
+
+#ifdef TF_RAID_MODE
+
+//---------------------------------------------------------------------------------------------
+class CBossAlphaLaunchRockets : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ // if anything interrupts this action, abort it
+ virtual ActionResult< CBossAlpha > OnSuspend( CBossAlpha *me, Action< CBossAlpha > *interruptingAction ) { return Done(); }
+
+ virtual const char *GetName( void ) const { return "LaunchRockets"; } // return name of this action
+
+private:
+ CountdownTimer m_timer;
+
+ CountdownTimer m_launchTimer;
+ int m_rocketsLeft;
+
+ int m_animLayer;
+
+ CHandle< CBaseCombatCharacter > m_target;
+ Vector m_lastTargetPosition;
+};
+
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_LAUNCH_ROCKETS_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.cpp
new file mode 100644
index 0000000..f935cfa
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.cpp
@@ -0,0 +1,64 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_lost_victim.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h"
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLostVictim::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ m_headTurn = 0.0f;
+ m_headYawPoseParameter = me->LookupPoseParameter( "body_yaw" );
+
+ m_timer.Start( RandomFloat( 3.0f, 5.0f ) );
+
+ me->EmitSound( "RobotBoss.Scanning" );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaLostVictim::Update( CBossAlpha *me, float interval )
+{
+ if ( m_timer.IsElapsed() )
+ {
+ return Done( "Giving up" );
+ }
+
+ CBaseCombatCharacter *target = me->GetAttackTarget();
+ if ( target )
+ {
+ if ( me->IsLineOfSightClear( target ) || me->IsPrisonerOfMinion( target ) )
+ {
+ me->EmitSound( "RobotBoss.Acquire" );
+ me->AddGesture( ACT_MP_GESTURE_FLINCH_CHEST );
+ return Done( "Ah hah!" );
+ }
+ }
+
+ const float rate = M_PI / 3.0f;
+ m_headTurn += rate * interval;
+
+ float s, c;
+ SinCos( m_headTurn, &s, &c );
+
+ me->SetPoseParameter( m_headYawPoseParameter, 40.0f * s );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaLostVictim::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->SetPoseParameter( m_headYawPoseParameter, 0 );
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h
new file mode 100644
index 0000000..b5be9c1
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_lost_victim.h
@@ -0,0 +1,28 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_lost_victim.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_LOST_VICTIM_H
+#define BOSS_ALPHA_LOST_VICTIM_H
+
+#ifdef TF_RAID_MODE
+
+//---------------------------------------------------------------------------------------------
+class CBossAlphaLostVictim : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ virtual const char *GetName( void ) const { return "LostVictim"; } // return name of this action
+
+private:
+ CountdownTimer m_timer;
+ float m_headTurn;
+ int m_headYawPoseParameter;
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_LOST_VICTIM_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.cpp
new file mode 100644
index 0000000..f1a3289
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.cpp
@@ -0,0 +1,208 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_nuke_attack.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_player.h"
+#include "tf_team.h"
+#include "player_vs_environment/monster_resource.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h"
+
+
+ConVar tf_boss_alpha_nuke_charge_time( "tf_boss_alpha_nuke_charge_time", "5" );
+ConVar tf_boss_alpha_nuke_interval( "tf_boss_alpha_nuke_interval", "20" );
+ConVar tf_boss_alpha_nuke_lethal_time( "tf_boss_alpha_nuke_lethal_time", "999999999" ); // 300
+ConVar tf_boss_alpha_nuke_damage( "tf_boss_alpha_nuke_damage", "75"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_nuke_max_remaining_health( "tf_boss_alpha_nuke_max_remaining_health", "60"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_nuke_afterburn_time( "tf_boss_alpha_nuke_afterburn_time", "5"/*, FCVAR_CHEAT*/ );
+
+extern ConVar tf_boss_alpha_stunned_duration;
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaNukeAttack::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ me->GetBodyInterface()->StartActivity( ACT_MP_JUMP_FLOAT_LOSERSTATE );
+ me->StartNukeEffect();
+
+ me->EmitSound( "RobotBoss.ChargeUpNukeAttack" );
+ // me->AddCondition( CBossAlpha::VULNERABLE_TO_STUN );
+
+ m_chargeUpTimer.Start( tf_boss_alpha_nuke_charge_time.GetFloat() );
+ m_shakeTimer.Start( 0.25f );
+
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaNukeAttack::Update( CBossAlpha *me, float interval )
+{
+ float stunRatio = me->GetStunDamage() / me->GetBecomeStunnedDamage();
+
+ if ( me->HasAbility( CBossAlpha::CAN_BE_STUNNED ) && stunRatio >= 1.0f )
+ {
+ return ChangeTo( new CBossAlphaStunned( tf_boss_alpha_stunned_duration.GetFloat() ), "They got me" );
+ }
+
+ // update the client's HUD
+ if ( g_pMonsterResource )
+ {
+ g_pMonsterResource->SetBossStunPercentage( 1.0f - stunRatio );
+ }
+
+ if ( m_shakeTimer.IsElapsed() )
+ {
+ m_shakeTimer.Reset();
+ UTIL_ScreenShake( me->GetAbsOrigin(), 15.0f, 5.0f, 1.0f, 3000.0f, SHAKE_START );
+ }
+
+ if ( m_chargeUpTimer.IsElapsed() )
+ {
+ // BLAST!
+ CUtlVector< CTFPlayer * > playerVector;
+ CollectPlayers( &playerVector, TF_TEAM_RED, COLLECT_ONLY_LIVING_PLAYERS );
+ CollectPlayers( &playerVector, TF_TEAM_BLUE, COLLECT_ONLY_LIVING_PLAYERS, APPEND_PLAYERS );
+
+ me->EmitSound( "RobotBoss.NukeAttack" );
+
+ CUtlVector< CBaseCombatCharacter * > victimVector;
+
+ int i;
+
+ // players
+ for ( i=0; i<playerVector.Count(); ++i )
+ {
+ CBasePlayer *player = playerVector[i];
+
+ if ( player && player->IsAlive() && player->GetTeamNumber() == TF_TEAM_BLUE )
+ {
+ victimVector.AddToTail( player );
+ }
+ }
+
+ // 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 );
+ }
+ }
+ }
+
+#ifdef SKIPME
+ 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 );
+ }
+ }
+#endif // SKIPME
+
+ for( int i=0; i<victimVector.Count(); ++i )
+ {
+ CBaseCombatCharacter *victim = victimVector[i];
+
+ if ( me->IsSelf( victim ) )
+ continue;
+
+ if ( me->IsLineOfSightClear( victim ) )
+ {
+ Vector toVictim = victim->WorldSpaceCenter() - me->WorldSpaceCenter();
+ toVictim.NormalizeInPlace();
+
+ float damage = tf_boss_alpha_nuke_damage.GetFloat();
+
+ if ( me->GetAge() > tf_boss_alpha_nuke_lethal_time.GetFloat() )
+ {
+ // nuke is now lethal
+ damage = 999.9f;
+ }
+ else if ( tf_boss_alpha_nuke_max_remaining_health.GetFloat() >= 0.0f )
+ {
+ // nuke slams everyone's health to this
+ if ( victim->GetHealth() > tf_boss_alpha_nuke_max_remaining_health.GetFloat() )
+ {
+ damage = victim->GetHealth() - tf_boss_alpha_nuke_max_remaining_health.GetFloat();
+ }
+ }
+
+ CTakeDamageInfo info( me, me, damage, DMG_ENERGYBEAM, TF_DMG_CUSTOM_NONE );
+ CalculateMeleeDamageForce( &info, toVictim, me->WorldSpaceCenter(), 1.0f );
+ victim->TakeDamage( info );
+
+ if ( victim->IsPlayer() )
+ {
+ CTFPlayer *playerVictim = ToTFPlayer( victim );
+
+ // catch them on fire (unless they are a Pyro)
+ if ( !playerVictim->IsPlayerClass( TF_CLASS_PYRO ) )
+ {
+ playerVictim->m_Shared.Burn( me, tf_boss_alpha_nuke_afterburn_time.GetFloat() );
+ }
+
+ color32 colorHit = { 255, 255, 255, 255 };
+ UTIL_ScreenFade( victim, colorHit, 1.0f, 0.1f, FFADE_IN );
+ }
+ }
+ }
+
+ return Done();
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaNukeAttack::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->RemoveCondition( CBossAlpha::VULNERABLE_TO_STUN );
+ me->StopNukeEffect();
+ me->ClearStunDamage();
+ me->GetNukeTimer()->Start( tf_boss_alpha_nuke_interval.GetFloat() );
+
+ if ( g_pMonsterResource )
+ {
+ g_pMonsterResource->HideBossStunMeter();
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaNukeAttack::OnInjured( CBossAlpha *me, const CTakeDamageInfo &info )
+{
+ return TryToSustain( RESULT_CRITICAL );
+}
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h
new file mode 100644
index 0000000..3566a59
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_nuke_attack.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_nuke_attack.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_NUKE_ATTACK_H
+#define BOSS_ALPHA_NUKE_ATTACK_H
+
+#ifdef TF_RAID_MODE
+
+class CBossAlphaNukeAttack : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ // if anything interrupts this action, abort it
+ virtual ActionResult< CBossAlpha > OnSuspend( CBossAlpha *me, Action< CBossAlpha > *interruptingAction ) { return Done(); }
+
+ virtual EventDesiredResult< CBossAlpha > OnInjured( CBossAlpha *me, const CTakeDamageInfo &info );
+
+ virtual const char *GetName( void ) const { return "NukeAttack"; } // return name of this action
+
+private:
+ CountdownTimer m_shakeTimer;
+ CountdownTimer m_chargeUpTimer;
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_NUKE_ATTACK_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.cpp
new file mode 100644
index 0000000..19f340f
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.cpp
@@ -0,0 +1,171 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_stunned.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_shareddefs.h"
+#include "tf_ammo_pack.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h"
+
+
+extern ConVar tf_boss_alpha_min_nuke_after_stun_time;
+
+
+//---------------------------------------------------------------------------------------------
+CBossAlphaStunned::CBossAlphaStunned( float duration, Action< CBossAlpha > *nextAction )
+{
+ m_timer.Start( duration );
+ m_nextAction = nextAction;
+}
+
+
+//---------------------------------------------------------------------------------------------
+ConVar tf_boss_alpha_stun_ammo_count( "tf_boss_alpha_stun_ammo_count", "3"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_stun_ammo_amount( "tf_boss_alpha_stun_ammo_amount", "100"/*, FCVAR_CHEAT*/ );
+ConVar tf_boss_alpha_stun_ammo_velocity( "tf_boss_alpha_stun_ammo_velocity", "100"/*, FCVAR_CHEAT*/ );
+
+void TossAmmoPack( CBossAlpha *me )
+{
+ int iPrimary = tf_boss_alpha_stun_ammo_amount.GetInt();
+ int iSecondary = tf_boss_alpha_stun_ammo_amount.GetInt();
+ int iMetal = tf_boss_alpha_stun_ammo_amount.GetInt();
+
+ // Create the ammo pack.
+ CTFAmmoPack *pAmmoPack = CTFAmmoPack::Create( me->GetAbsOrigin(), me->GetAbsAngles(), NULL, "models/items/ammopack_medium.mdl" );
+ if ( pAmmoPack )
+ {
+/*
+ Vector vel;
+
+ vel.x = RandomFloat( -1.0f, 1.0f ) * tf_boss_alpha_stun_ammo_velocity.GetFloat();
+ vel.y = RandomFloat( -1.0f, 1.0f ) * tf_boss_alpha_stun_ammo_velocity.GetFloat();
+ vel.z = tf_boss_alpha_stun_ammo_velocity.GetFloat();
+
+ pAmmoPack->SetInitialVelocity( vel );
+*/
+ pAmmoPack->m_nSkin = 0;
+
+ // Give the ammo pack some health, so that trains can destroy it.
+ pAmmoPack->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ pAmmoPack->m_takedamage = DAMAGE_YES;
+ pAmmoPack->SetHealth( 900 );
+
+ pAmmoPack->SetBodygroup( 1, 1 );
+
+ pAmmoPack->ApplyLocalAngularVelocityImpulse( AngularImpulse( 600, random->RandomInt( -1200, 1200 ), 0 ) );
+
+ DispatchSpawn( pAmmoPack );
+
+ // Fill up the ammo pack.
+ pAmmoPack->GiveAmmo( iPrimary, TF_AMMO_PRIMARY );
+ pAmmoPack->GiveAmmo( iSecondary, TF_AMMO_SECONDARY );
+ pAmmoPack->GiveAmmo( iMetal, TF_AMMO_METAL );
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaStunned::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ // start animation
+ me->GetBodyInterface()->StartActivity( ACT_MP_STAND_MELEE );
+ m_layerUsed = me->AddLayeredSequence( me->LookupSequence( "PRIMARY_Stun_begin" ), 0 );
+ m_state = BECOMING_STUNNED;
+
+ m_timer.Reset();
+
+ me->AddCondition( CBossAlpha::STUNNED );
+ me->EmitSound( "RobotBoss.StunStart" );
+
+ // throw out some ammo
+ for( int i=0; i<tf_boss_alpha_stun_ammo_count.GetInt(); ++i )
+ {
+ TossAmmoPack( me );
+ }
+
+ // relay the event to the map logic
+ me->m_outputOnStunned.FireOutput( me, me );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaStunned::Update( CBossAlpha *me, float interval )
+{
+ switch( m_state )
+ {
+ case BECOMING_STUNNED:
+ if ( me->IsSequenceFinished() )
+ {
+ me->FastRemoveLayer( m_layerUsed );
+
+ m_state = STUNNED;
+ m_layerUsed = me->AddLayeredSequence( me->LookupSequence( "PRIMARY_stun_middle" ), 0 );
+ me->SetLayerLooping( m_layerUsed, true );
+ me->EmitSound( "RobotBoss.Stunned" );
+ }
+ break;
+
+ case STUNNED:
+ if ( m_timer.IsElapsed() )
+ {
+ me->FastRemoveLayer( m_layerUsed );
+
+ m_state = RECOVERING;
+ m_layerUsed = me->AddLayeredSequence( me->LookupSequence( "PRIMARY_stun_end" ), 0 );
+ me->StopSound( "RobotBoss.Stunned" );
+ me->EmitSound( "RobotBoss.StunRecover" );
+ }
+ break;
+
+ case RECOVERING:
+ if ( me->IsSequenceFinished() )
+ {
+ me->FastRemoveLayer( m_layerUsed );
+
+ if ( m_nextAction )
+ {
+ return ChangeTo( m_nextAction, "Stun finished" );
+ }
+
+ return Done( "Stun finished" );
+ }
+ break;
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaStunned::OnInjured( CBossAlpha *me, const CTakeDamageInfo &info )
+{
+ return TryToSustain( RESULT_CRITICAL );
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaStunned::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->RemoveCondition( CBossAlpha::STUNNED );
+
+ if ( me->HasAbility( CBossAlpha::CAN_ENRAGE ) )
+ {
+ // being stunned makes the boss ANGRY!
+ me->AddCondition( CBossAlpha::ENRAGED );
+ }
+
+ // make sure the boss attacks at least once before he starts a nuke
+ if ( me->GetNukeTimer()->GetRemainingTime() < tf_boss_alpha_min_nuke_after_stun_time.GetFloat() )
+ {
+ me->GetNukeTimer()->Start( tf_boss_alpha_min_nuke_after_stun_time.GetFloat() );
+ }
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h
new file mode 100644
index 0000000..d73ba17
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_stunned.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_STUNNED_H
+#define BOSS_ALPHA_STUNNED_H
+
+#ifdef TF_RAID_MODE
+
+class CBossAlphaStunned : public Action< CBossAlpha >
+{
+public:
+ CBossAlphaStunned( float duration, Action< CBossAlpha > *nextAction = NULL );
+
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ virtual EventDesiredResult< CBossAlpha > OnInjured( CBossAlpha *me, const CTakeDamageInfo &info );
+
+ virtual const char *GetName( void ) const { return "Stunned"; } // return name of this action
+
+private:
+ CountdownTimer m_timer;
+ enum StunStateType
+ {
+ BECOMING_STUNNED,
+ STUNNED,
+ RECOVERING
+ }
+ m_state;
+ int m_layerUsed;
+
+ Action< CBossAlpha > *m_nextAction;
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_STUNNED_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.cpp
new file mode 100644
index 0000000..eca4107
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.cpp
@@ -0,0 +1,81 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_tactical_monitor.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "tf_gamerules.h"
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_get_off_me.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_stunned.h"
+
+
+ConVar tf_boss_alpha_get_off_me_duration( "tf_boss_alpha_get_off_me_duration", "3"/*, FCVAR_CHEAT */ );
+ConVar tf_boss_alpha_stunned_duration( "tf_boss_alpha_stunned_duration", "10" );
+
+
+//---------------------------------------------------------------------------------------------
+Action< CBossAlpha > *CBossAlphaTacticalMonitor::InitialContainedAction( CBossAlpha *me )
+{
+ if ( TFGameRules()->IsBossBattleMode() )
+ {
+ return new CBossAlphaWaitForPlayers;
+ }
+
+ return NULL;
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaTacticalMonitor::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ m_getOffMeTimer.Invalidate();
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaTacticalMonitor::Update( CBossAlpha *me, float interval )
+{
+ if ( me->IsInCondition( CBossAlpha::STUNNED ) )
+ {
+ return SuspendFor( new CBossAlphaStunned( tf_boss_alpha_stunned_duration.GetFloat() ), "Ouch!" );
+ }
+
+ if ( !m_getOffMeTimer.HasStarted() )
+ {
+ CUtlVector< CTFPlayer * > onMeVector;
+ me->CollectPlayersStandingOnMe( &onMeVector );
+
+ if ( onMeVector.Count() )
+ {
+ // someone is standing on me - push them off soon
+ m_getOffMeTimer.Start( tf_boss_alpha_get_off_me_duration.GetFloat() );
+ }
+ }
+ else if ( m_getOffMeTimer.IsElapsed() )
+ {
+ if ( !me->IsBusy() )
+ {
+ m_getOffMeTimer.Invalidate();
+
+ // if someone is still on me, push them off
+ CUtlVector< CTFPlayer * > onMeVector;
+ me->CollectPlayersStandingOnMe( &onMeVector );
+ if ( onMeVector.Count() )
+ {
+ return SuspendFor( new CBossAlphaGetOffMe, "Get offa me!" );
+ }
+ }
+ }
+
+ return Continue();
+}
+
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h
new file mode 100644
index 0000000..e612b7c
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_tactical_monitor.h
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_tactical_monitor.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_TACTICAL_MONITOR_H
+#define BOSS_ALPHA_TACTICAL_MONITOR_H
+
+#ifdef TF_RAID_MODE
+
+class CBossAlpha;
+
+
+//---------------------------------------------------------------------------------------------
+class CBossAlphaTacticalMonitor : public Action< CBossAlpha >
+{
+public:
+ virtual Action< CBossAlpha > *InitialContainedAction( CBossAlpha *me );
+
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+
+ virtual const char *GetName( void ) const { return "TacticalMonitor"; } // return name of this action
+
+private:
+ CountdownTimer m_backOffCooldownTimer;
+ CountdownTimer m_getOffMeTimer;
+};
+
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_TACTICAL_MONITOR_H
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.cpp b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.cpp
new file mode 100644
index 0000000..cec965f
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.cpp
@@ -0,0 +1,79 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_wait_for_players.cpp
+// Michael Booth, November 2010
+
+#include "cbase.h"
+
+#ifdef TF_RAID_MODE
+
+#include "player_vs_environment/boss_alpha/boss_alpha.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h"
+#include "player_vs_environment/boss_alpha/behavior/boss_alpha_guard_spot.h"
+
+
+extern ConVar tf_boss_alpha_nuke_interval;
+
+ConVar tf_boss_alpha_sleep( "tf_boss_alpha_sleep", "0"/*, FCVAR_CHEAT */ );
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaWaitForPlayers::OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction )
+{
+ me->AddCondition( CBossAlpha::BUSY );
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+ActionResult< CBossAlpha > CBossAlphaWaitForPlayers::Update( CBossAlpha *me, float interval )
+{
+ if ( tf_boss_alpha_sleep.GetBool() )
+ {
+ return Continue();
+ }
+
+ CBaseCombatCharacter *target = me->GetAttackTarget();
+ if ( target )
+ {
+ return ChangeTo( new CBossAlphaGuardSpot, "I see you..." );
+ }
+
+ return Continue();
+}
+
+
+//---------------------------------------------------------------------------------------------
+void CBossAlphaWaitForPlayers::OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction )
+{
+ me->RemoveCondition( CBossAlpha::BUSY );
+
+ me->GetNukeTimer()->Start( tf_boss_alpha_nuke_interval.GetFloat() );
+ me->GetGrenadeTimer()->Reset();
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaWaitForPlayers::OnInjured( CBossAlpha *me, const CTakeDamageInfo &info )
+{
+ if ( tf_boss_alpha_sleep.GetBool() )
+ {
+ return TryContinue();
+ }
+
+ return TryChangeTo( new CBossAlphaGuardSpot, RESULT_CRITICAL, "Ouch!" );
+}
+
+
+//---------------------------------------------------------------------------------------------
+EventDesiredResult< CBossAlpha > CBossAlphaWaitForPlayers::OnContact( CBossAlpha *me, CBaseEntity *other, CGameTrace *result )
+{
+ if ( other && other->IsPlayer() && !tf_boss_alpha_sleep.GetBool() )
+ {
+ return TryChangeTo( new CBossAlphaGuardSpot, RESULT_CRITICAL, "Don't touch me" );
+ }
+
+ return TryContinue();
+}
+
+#endif // TF_RAID_MODE
diff --git a/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h
new file mode 100644
index 0000000..a24011d
--- /dev/null
+++ b/game/server/tf/player_vs_environment/boss_alpha/behavior/boss_alpha_wait_for_players.h
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+// boss_alpha_wait_for_players.h
+// Michael Booth, November 2010
+
+#ifndef BOSS_ALPHA_WAIT_FOR_PLAYER_H
+#define BOSS_ALPHA_WAIT_FOR_PLAYER_H
+
+#ifdef TF_RAID_MODE
+
+class CBossAlphaWaitForPlayers : public Action< CBossAlpha >
+{
+public:
+ virtual ActionResult< CBossAlpha > OnStart( CBossAlpha *me, Action< CBossAlpha > *priorAction );
+ virtual ActionResult< CBossAlpha > Update( CBossAlpha *me, float interval );
+ virtual void OnEnd( CBossAlpha *me, Action< CBossAlpha > *nextAction );
+
+ virtual EventDesiredResult< CBossAlpha > OnInjured( CBossAlpha *me, const CTakeDamageInfo &info );
+ virtual EventDesiredResult< CBossAlpha > OnContact( CBossAlpha *me, CBaseEntity *other, CGameTrace *result = NULL );
+
+ virtual const char *GetName( void ) const { return "WaitForPlayers"; } // return name of this action
+};
+
+#endif // TF_RAID_MODE
+
+#endif // BOSS_ALPHA_WAIT_FOR_PLAYER_H