summaryrefslogtreecommitdiff
path: root/game/shared/tf/entity_capture_flag.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf/entity_capture_flag.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/tf/entity_capture_flag.cpp')
-rw-r--r--game/shared/tf/entity_capture_flag.cpp3100
1 files changed, 3100 insertions, 0 deletions
diff --git a/game/shared/tf/entity_capture_flag.cpp b/game/shared/tf/entity_capture_flag.cpp
new file mode 100644
index 0000000..e858f2b
--- /dev/null
+++ b/game/shared/tf/entity_capture_flag.cpp
@@ -0,0 +1,3100 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: CTF Flag.
+//
+//=============================================================================//
+#include "cbase.h"
+#include "entity_capture_flag.h"
+#include "tf_gamerules.h"
+#include "tf_shareddefs.h"
+#include "filesystem.h"
+#include "tf_logic_player_destruction.h"
+
+#ifdef CLIENT_DLL
+#include <vgui_controls/Panel.h>
+#include <vgui_controls/ImagePanel.h>
+#include <vgui_controls/EditablePanel.h>
+#include <vgui/IScheme.h>
+#include "hudelement.h"
+#include "iclientmode.h"
+#include "hud_numericdisplay.h"
+#include "tf_imagepanel.h"
+#include "c_tf_player.h"
+#include "c_tf_team.h"
+#include "tf_hud_objectivestatus.h"
+#include "view.h"
+
+ConVar cl_flag_return_size( "cl_flag_return_size", "20", FCVAR_CHEAT );
+
+extern ConVar tf_rd_flag_ui_mode;
+
+#else
+#include "tf_player.h"
+#include "tf_team.h"
+#include "tf_objective_resource.h"
+#include "tf_gamestats.h"
+#include "func_respawnroom.h"
+#include "datacache/imdlcache.h"
+#include "func_respawnflag.h"
+#include "func_capture_zone.h"
+#include "nav_mesh/tf_nav_mesh.h"
+#include "player_vs_environment/tf_population_manager.h"
+#include "tf_logic_robot_destruction.h"
+#include "tf_logic_halloween_2014.h"
+extern ConVar tf_flag_caps_per_round;
+extern ConVar tf_mvm_endless_bomb_reset;
+extern ConVar tf_rd_min_points_to_steal;
+
+ConVar cl_flag_return_height( "cl_flag_return_height", "82", FCVAR_CHEAT );
+ConVar tf_rd_return_min_time( "tf_rd_return_min_time", "30" );
+ConVar tf_rd_return_max_time( "tf_rd_return_max_time", "90" );
+
+#endif
+
+ConVar tf_flag_return_on_touch( "tf_flag_return_on_touch", "0", FCVAR_REPLICATED, "If this is set, your flag must be at base in order to capture the enemy flag. Remote friendly flags return to your base instantly when you touch them" );
+
+#ifdef STAGING_ONLY
+ConVar tf_flag_return_time_override( "tf_flag_return_time_override", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "How long before a dropped flag will return (in seconds). 0 = Use map/default settings. For internal-use only.", true, 0.f, false, 0.f );
+#endif // STAGING_ONLY
+
+ConVar tf_flag_return_time_credit_factor( "tf_flag_return_time_credit_factor", "1.0", FCVAR_REPLICATED, "Number of seconds the flag's return time will be credited for each second the flag is being carried.", true, 0.f, false, 0.f );
+
+enum
+{
+ INVADE_NEUTRAL_TYPE_NONE = 0, // no neutral time
+ INVADE_NEUTRAL_TYPE_DEFAULT, // current behavior....30 secs (TF_INVADE_NEUTRAL_TIME)
+ INVADE_NEUTRAL_TYPE_HALF, // half the return time
+};
+
+enum
+{
+ INVADE_SCORING_TEAM_SCORE = 0,
+ INVADE_SCORING_TEAM_CAPTURE_COUNT,
+};
+
+#define FLAG_EFFECTS_NONE 0
+#define FLAG_EFFECTS_ALL 1
+#define FLAG_EFFECTS_PAPERONLY 2
+#define FLAG_EFFECTS_COLORONLY 3
+
+#ifdef CLIENT_DLL
+
+static void RecvProxy_IsDisabled( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
+ bool bIsDisabled = ( pData->m_Value.m_Int > 0 );
+
+ if ( pFlag )
+ {
+ pFlag->SetDisabled( bIsDisabled );
+ }
+}
+
+static void RecvProxy_IsVisibleWhenDisabled( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
+ bool bVisible = ( pData->m_Value.m_Int > 0 );
+
+ if ( pFlag )
+ {
+ pFlag->SetVisibleWhenDisabled( bVisible );
+ }
+}
+
+static void RecvProxy_FlagStatus( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
+
+ if ( pFlag )
+ {
+ pFlag->SetFlagStatus( pData->m_Value.m_Int );
+
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "entindex", pFlag->entindex() );
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+ }
+}
+
+#endif
+
+//=============================================================================
+//
+// CTF Flag tables.
+//
+
+IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlag, DT_CaptureFlag )
+
+BEGIN_NETWORK_TABLE( CCaptureFlag, DT_CaptureFlag )
+
+#ifdef GAME_DLL
+ SendPropBool( SENDINFO( m_bDisabled ) ),
+ SendPropBool( SENDINFO( m_bVisibleWhenDisabled ) ),
+ SendPropInt( SENDINFO( m_nType ), 5, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_nFlagStatus ), 3, SPROP_UNSIGNED ),
+ SendPropTime( SENDINFO( m_flResetTime ) ),
+ SendPropTime( SENDINFO( m_flNeutralTime ) ),
+ SendPropTime( SENDINFO( m_flMaxResetTime ) ),
+ SendPropEHandle( SENDINFO( m_hPrevOwner ) ),
+ SendPropString( SENDINFO( m_szModel ) ),
+ SendPropString( SENDINFO( m_szHudIcon ) ),
+ SendPropString( SENDINFO( m_szPaperEffect ) ),
+ SendPropString( SENDINFO( m_szTrailEffect ) ),
+ SendPropInt( SENDINFO( m_nUseTrailEffect ), 3, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_nPointValue ) ),
+ SendPropFloat( SENDINFO( m_flAutoCapTime ) ),
+ SendPropBool( SENDINFO( m_bGlowEnabled ) ),
+ SendPropFloat( SENDINFO( m_flTimeToSetPoisonous ) ),
+
+#else
+ RecvPropInt( RECVINFO( m_bDisabled ), 0, RecvProxy_IsDisabled ),
+ RecvPropInt( RECVINFO( m_bVisibleWhenDisabled ), 0, RecvProxy_IsVisibleWhenDisabled ),
+ RecvPropInt( RECVINFO( m_nType ) ),
+ RecvPropInt( RECVINFO( m_nFlagStatus ), 0, RecvProxy_FlagStatus ),
+ RecvPropTime( RECVINFO( m_flResetTime ) ),
+ RecvPropTime( RECVINFO( m_flNeutralTime ) ),
+ RecvPropTime( RECVINFO( m_flMaxResetTime ) ),
+ RecvPropEHandle( RECVINFO( m_hPrevOwner ) ),
+ RecvPropString( RECVINFO( m_szModel ) ),
+ RecvPropString( RECVINFO( m_szHudIcon ) ),
+ RecvPropString( RECVINFO( m_szPaperEffect ) ),
+ RecvPropString( RECVINFO( m_szTrailEffect ) ),
+ RecvPropInt( RECVINFO( m_nUseTrailEffect ) ),
+ RecvPropInt( RECVINFO( m_nPointValue ) ),
+ RecvPropFloat( RECVINFO( m_flAutoCapTime ) ),
+ RecvPropBool( RECVINFO( m_bGlowEnabled ) ),
+ RecvPropFloat( RECVINFO( m_flTimeToSetPoisonous ) ),
+
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_DATADESC( CCaptureFlag )
+
+ // Keyfields.
+ DEFINE_KEYFIELD( m_nType, FIELD_INTEGER, "GameType" ),
+ DEFINE_KEYFIELD( m_nReturnTime, FIELD_INTEGER, "ReturnTime" ),
+ DEFINE_KEYFIELD( m_nUseTrailEffect, FIELD_INTEGER, "trail_effect" ),
+ DEFINE_KEYFIELD( m_nNeutralType, FIELD_INTEGER, "NeutralType" ),
+ DEFINE_KEYFIELD( m_nScoringType, FIELD_INTEGER, "ScoringType" ),
+ DEFINE_KEYFIELD( m_bReturnBetweenWaves, FIELD_BOOLEAN, "ReturnBetweenWaves" ),
+ DEFINE_KEYFIELD( m_bVisibleWhenDisabled, FIELD_BOOLEAN, "VisibleWhenDisabled" ),
+ DEFINE_KEYFIELD( m_bUseShotClockMode, FIELD_BOOLEAN, "ShotClockMode" ),
+ DEFINE_KEYFIELD( m_nPointValue, FIELD_INTEGER, "PointValue" ),
+
+#ifdef GAME_DLL
+ DEFINE_KEYFIELD( m_iszModel, FIELD_STRING, "flag_model" ),
+ DEFINE_KEYFIELD( m_iszHudIcon, FIELD_STRING, "flag_icon" ),
+ DEFINE_KEYFIELD( m_iszPaperEffect, FIELD_STRING, "flag_paper" ),
+ DEFINE_KEYFIELD( m_iszTrailEffect, FIELD_STRING, "flag_trail" ),
+ DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ),
+
+ // Inputs.
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ForceDrop", InputForceDrop ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ForceReset", InputForceReset ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ForceResetSilent", InputForceResetSilent ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "ForceResetAndDisableSilent", InputForceResetAndDisableSilent ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "SetReturnTime", InputSetReturnTime ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "ShowTimer", InputShowTimer ),
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "ForceGlowDisabled", InputForceGlowDisabled ),
+
+ // Outputs.
+ DEFINE_OUTPUT( m_outputOnReturn, "OnReturn" ),
+ DEFINE_OUTPUT( m_outputOnPickUp, "OnPickUp" ),
+ DEFINE_OUTPUT( m_outputOnPickUpTeam1, "OnPickupTeam1" ),
+ DEFINE_OUTPUT( m_outputOnPickUpTeam2, "OnPickupTeam2" ),
+ DEFINE_OUTPUT( m_outputOnDrop, "OnDrop" ),
+ DEFINE_OUTPUT( m_outputOnCapture, "OnCapture" ),
+ DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ),
+ DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
+ DEFINE_OUTPUT( m_OnTouchSameTeam, "OnTouchSameTeam" ),
+#endif
+
+END_DATADESC();
+
+LINK_ENTITY_TO_CLASS( item_teamflag, CCaptureFlag );
+
+IMPLEMENT_AUTO_LIST( ICaptureFlagAutoList );
+
+//=============================================================================
+//
+// CTF Flag functions.
+//
+
+CCaptureFlag::CCaptureFlag()
+{
+#ifdef CLIENT_DLL
+ m_pGlowTrailEffect = NULL;
+ m_pPaperTrailEffect = NULL;
+ m_pGlowEffect = NULL;
+ m_hOldOwner = NULL;
+ m_bOldGlowEnabled = true;
+#else
+ m_hReturnIcon = NULL;
+ m_nReturnTime = 60;
+ m_hInitialPlayer = NULL;
+ m_hInitialParent = NULL;
+ m_vecOffset.Init( 0, 0, 0 );
+
+ m_iszModel = NULL_STRING;
+ m_iszHudIcon = NULL_STRING;
+ m_iszPaperEffect = NULL_STRING;
+ m_iszTrailEffect = NULL_STRING;
+ m_nPointValue = 0;
+ m_flTimeToSetPoisonous = 0.f;
+
+ // Team specific sound throttling for Special Delivery
+ for ( int i = 0; i < ARRAYSIZE( m_flNextTeamSoundTime ); i++ )
+ {
+ m_flNextTeamSoundTime[i] = 0.f;
+ }
+#endif
+
+ m_nNeutralType = INVADE_NEUTRAL_TYPE_DEFAULT;
+ m_nScoringType = INVADE_SCORING_TEAM_SCORE;
+ m_bReturnBetweenWaves = true;
+ m_bVisibleWhenDisabled = false;
+ m_bUseShotClockMode = false;
+ m_bGlowEnabled = true;
+
+ UseClientSideAnimation();
+
+ m_szModel.GetForModify()[ 0 ] = '\0';
+ m_szHudIcon.GetForModify()[ 0 ] = '\0';
+ m_szPaperEffect.GetForModify()[ 0 ] = '\0';
+ m_szTrailEffect.GetForModify()[ 0 ] = '\0';
+ m_nUseTrailEffect.Set( FLAG_EFFECTS_ALL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CCaptureFlag::~CCaptureFlag()
+{
+#ifndef GAME_DLL
+ if ( m_pGlowEffect )
+ {
+ delete m_pGlowEffect;
+ m_pGlowEffect = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+unsigned int CCaptureFlag::GetItemID( void ) const
+{
+ return TF_ITEM_CAPTURE_FLAG;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CCaptureFlag::GetFlagModel( void )
+{
+ if ( m_szModel[ 0 ] != '\0' )
+ {
+ if ( g_pFullFileSystem->FileExists( m_szModel.Get(), "GAME" ) )
+ {
+ return ( m_szModel.Get() );
+ }
+ }
+
+ return TF_FLAG_MODEL;
+}
+
+void CCaptureFlag::GetHudIcon( int nTeam, char *pchName, int nBuffSize )
+{
+ V_snprintf( pchName, nBuffSize, "%s_%s",
+ ( ( m_szHudIcon[ 0 ] != '\0' ) ? ( m_szHudIcon.Get() ) : ( TF_FLAG_ICON ) ),
+ ( ( nTeam == TF_TEAM_BLUE ) ? ( "blue" ) : ( "red" ) ) );
+}
+
+const char *CCaptureFlag::GetPaperEffect( void )
+{
+ if ( m_szPaperEffect[ 0 ] != '\0' )
+ {
+ return ( m_szPaperEffect.Get() );
+ }
+
+ return TF_FLAG_EFFECT;
+}
+
+void CCaptureFlag::GetTrailEffect( int nTeam, char *pchName, int nBuffSize )
+{
+ V_snprintf( pchName, nBuffSize, "effects/%s_%s.vmt",
+ ( ( m_szTrailEffect[ 0 ] != '\0' ) ? ( m_szTrailEffect.Get() ) : ( TF_FLAG_TRAIL ) ),
+ ( ( nTeam == TF_TEAM_RED ) ? ( "red" ) : ( "blu" ) ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache the model and sounds.
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Precache( void )
+{
+ PrecacheModel( GetFlagModel() );
+
+ PrecacheScriptSound( TF_CTF_FLAGSPAWN );
+ PrecacheScriptSound( TF_CTF_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_CTF_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_CTF_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_CTF_ENEMY_RETURNED );
+ PrecacheScriptSound( TF_CTF_TEAM_STOLEN );
+ PrecacheScriptSound( TF_CTF_TEAM_DROPPED );
+ PrecacheScriptSound( TF_CTF_TEAM_CAPTURED );
+ PrecacheScriptSound( TF_CTF_TEAM_RETURNED );
+
+ PrecacheScriptSound( TF_AD_CAPTURED_SOUND );
+ PrecacheScriptSound( TF_AD_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_AD_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_AD_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_AD_ENEMY_RETURNED );
+ PrecacheScriptSound( TF_AD_TEAM_STOLEN );
+ PrecacheScriptSound( TF_AD_TEAM_DROPPED );
+ PrecacheScriptSound( TF_AD_TEAM_CAPTURED );
+ PrecacheScriptSound( TF_AD_TEAM_RETURNED );
+
+ PrecacheScriptSound( TF_MVM_AD_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_MVM_AD_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_MVM_AD_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_MVM_AD_ENEMY_RETURNED );
+
+ PrecacheScriptSound( TF_INVADE_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_INVADE_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_INVADE_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_INVADE_TEAM_STOLEN );
+ PrecacheScriptSound( TF_INVADE_TEAM_DROPPED );
+ PrecacheScriptSound( TF_INVADE_TEAM_CAPTURED );
+ PrecacheScriptSound( TF_INVADE_FLAG_RETURNED );
+
+ PrecacheScriptSound( TF_RESOURCE_FLAGSPAWN );
+ PrecacheScriptSound( TF_RESOURCE_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_RESOURCE_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_RESOURCE_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_RESOURCE_TEAM_STOLEN );
+ PrecacheScriptSound( TF_RESOURCE_TEAM_DROPPED );
+ PrecacheScriptSound( TF_RESOURCE_TEAM_CAPTURED );
+ PrecacheScriptSound( TF_RESOURCE_RETURNED );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_TEAM_STOLEN );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_TEAM_DROPPED );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_RETURNED );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_NAGS );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_RED_CAPPED );
+ PrecacheScriptSound( TF_RESOURCE_EVENT_BLUE_CAPPED );
+
+ PrecacheScriptSound( TF_RD_ENEMY_STOLEN );
+ PrecacheScriptSound( TF_RD_ENEMY_DROPPED );
+ PrecacheScriptSound( TF_RD_ENEMY_CAPTURED );
+ PrecacheScriptSound( TF_RD_ENEMY_RETURNED );
+ PrecacheScriptSound( TF_RD_TEAM_STOLEN );
+ PrecacheScriptSound( TF_RD_TEAM_DROPPED );
+ PrecacheScriptSound( TF_RD_TEAM_CAPTURED );
+ PrecacheScriptSound( TF_RD_TEAM_RETURNED );
+
+ PrecacheScriptSound( TF_RUNE_INTEL_CAPTURED );
+
+ PrecacheParticleSystem( GetPaperEffect() );
+
+ char szFileName[ MAX_PATH ];
+ GetTrailEffect( TF_TEAM_BLUE, szFileName, sizeof( szFileName ) );
+ PrecacheModel( szFileName );
+
+ GetTrailEffect( TF_TEAM_RED, szFileName, sizeof( szFileName ) );
+ PrecacheModel( szFileName );
+}
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::ShouldDraw()
+{
+ // don't draw flag on player in PD
+ if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( GetMoveParent() && GetMoveParent()->IsPlayer() )
+ {
+ return false;
+ }
+ }
+
+ return BaseClass::ShouldDraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::IsVisibleToTargetID() const
+{
+ return !IsDisabled() && GetPointValue() > 0 && const_cast<CCaptureFlag*>( this )->ShouldDraw();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ m_nOldTeamNumber = GetTeamNumber();
+ m_bOldGlowEnabled = m_bGlowEnabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::OnDataChanged( DataUpdateType_t updateType )
+{
+ bool bUpdateGlow = false;
+
+ if ( m_nOldTeamNumber != GetTeamNumber() )
+ {
+ bUpdateGlow = true;
+ }
+ else if ( m_hOldOwner.Get() != GetOwnerEntity() )
+ {
+ bUpdateGlow = true;
+ m_hOldOwner = GetOwnerEntity();
+ }
+ else if ( m_bOldGlowEnabled != m_bGlowEnabled )
+ {
+ bUpdateGlow = true;
+ }
+
+ if ( bUpdateGlow )
+ {
+ UpdateGlowEffect();
+ }
+
+ CreateSiren();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::CreateSiren( void )
+{
+ if ( m_hSirenEffect )
+ return;
+
+ int iAttachment = GetBaseAnimating()->LookupAttachment( "siren" );
+ if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
+ {
+ const char* flashlightName = "cart_flashinglight";
+ if ( GetTeamNumber() == TF_TEAM_RED )
+ {
+ flashlightName = "cart_flashinglight_red";
+ }
+ m_hSirenEffect = ParticleProp()->Create( flashlightName, PATTACH_POINT_FOLLOW, iAttachment );
+ }
+}
+
+void CCaptureFlag::DestroySiren( void )
+{
+ if ( m_hSirenEffect )
+ {
+ ParticleProp()->StopEmission( m_hSirenEffect );
+ m_hSirenEffect = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::UpdateGlowEffect( void )
+{
+ if ( !m_pGlowEffect )
+ {
+ m_pGlowEffect = new CGlowObject( this, Vector( 0.76f, 0.76f, 0.76f ), 1.0, true );
+ }
+
+ if ( m_pGlowEffect )
+ {
+ if ( ShouldHideGlowEffect() )
+ {
+ m_pGlowEffect->SetEntity( NULL );
+ }
+ else
+ {
+ m_pGlowEffect->SetEntity( this );
+
+ float r, g, b;
+ TeamplayRoundBasedRules()->GetTeamGlowColor( GetTeamNumber(), r, g, b );
+ m_pGlowEffect->SetColor( Vector( r, g, b ) );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::ShouldHideGlowEffect( void )
+{
+ if ( !IsGlowEnabled() )
+ {
+ return true;
+ }
+
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
+ {
+ return false;
+ }
+
+ if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION && tf_rd_flag_ui_mode.GetInt() )
+ return true;
+
+ if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION && CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
+ {
+ C_TFPlayer *pOwner = ToTFPlayer( m_hPrevOwner );
+ if ( !pOwner )
+ return true;
+
+ return CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetTeamLeader( pOwner->GetTeamNumber() ) != pOwner;
+ }
+
+ // If the opposite team stole our intel we need to hide the glow
+ bool bIsHiddenTeam = false;
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( pLocalPlayer )
+ {
+ if ( m_nType == TF_FLAGTYPE_CTF )
+ {
+ // In CTF the flag is the team of where it was originally sitting
+ bIsHiddenTeam = ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() );
+ }
+ else
+ {
+ // In non-CTF control the flag changes to the team that's carrying it
+ bIsHiddenTeam = ( pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR && pLocalPlayer->GetTeamNumber() != GetTeamNumber() );
+ }
+
+ if ( pLocalPlayer->m_Shared.IsFullyInvisible() )
+ {
+ C_TFPlayer *pOwner = ToTFPlayer( m_hPrevOwner );
+ if ( pOwner && pOwner != pLocalPlayer )
+ return true;
+ }
+ }
+
+ bool bHide = IsStolen() && bIsHiddenTeam;
+
+ if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
+ {
+ bHide = IsHome();
+ }
+
+ return ( IsDisabled() || bHide || IsEffectActive( EF_NODRAW ) );
+}
+#endif //#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Spawn( void )
+{
+#ifdef GAME_DLL
+ V_strncpy( m_szModel.GetForModify(), STRING( m_iszModel ), MAX_PATH );
+ V_strncpy( m_szHudIcon.GetForModify(), STRING( m_iszHudIcon ), MAX_PATH );
+ V_strncpy( m_szPaperEffect.GetForModify(), STRING( m_iszPaperEffect ), MAX_PATH );
+ V_strncpy( m_szTrailEffect.GetForModify(), STRING( m_iszTrailEffect ), MAX_PATH );
+#endif
+
+ // Precache the model and sounds. Set the flag model.
+ Precache();
+ SetModel( GetFlagModel() );
+
+ // Set the flag solid and the size for touching.
+ SetSolid( SOLID_BBOX );
+#ifdef STAGING_ONLY
+ SetSolidFlags( FSOLID_TRIGGER );
+ SetSize( vec3_origin, vec3_origin );
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+#else
+ SetSolidFlags( FSOLID_NOT_SOLID | FSOLID_TRIGGER );
+ SetSize( vec3_origin, vec3_origin );
+#endif
+
+ // Bloat the box for player pickup
+ CollisionProp()->UseTriggerBounds( true, 24 );
+
+ // use the initial dynamic prop "m_bStartDisabled" setting to set our own m_bDisabled flag
+#ifdef GAME_DLL
+ m_bDisabled = m_bStartDisabled;
+ m_bStartDisabled = false;
+ m_bInstantTrailRemove = false;
+ m_flTimeToSetPoisonous = 0.f;
+
+ // Don't allow the intelligence to fade.
+ m_flFadeScale = 0.0f;
+#else
+ m_bDisabled = false;
+#endif
+
+ // Base class spawn.
+ BaseClass::Spawn();
+
+ // Force specific collision bounds!
+ // This is to prevent a case where the flag can fall through the world
+ // If the model's bounds reach outside the player's from player center
+ SetCollisionBounds( Vector( -19.5f, -22.5f, -6.5f ), Vector( 19.5f, 22.5f, 6.5f ) );
+
+#ifdef GAME_DLL
+ // Save the starting position, so we can reset the flag later if need be.
+ m_vecResetPos = GetAbsOrigin();
+ m_vecResetAng = GetAbsAngles();
+
+ CBaseEntity *pParent = GetParent();
+ if ( pParent )
+ {
+ m_hInitialParent = pParent;
+ m_vecOffset = GetAbsOrigin() - pParent->GetAbsOrigin();
+ }
+
+ SetFlagStatus( TF_FLAGINFO_HOME );
+ ResetFlagReturnTime();
+ ResetFlagNeutralTime();
+
+ m_hInitialPlayer = NULL;
+
+ m_bAllowOwnerPickup = true;
+ m_hPrevOwner = NULL;
+
+ m_bCaptured = false;
+
+ // update the objective resource so clients have the information
+ if ( TFObjectiveResource() )
+ {
+ TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
+ TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
+ TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
+ }
+
+ const char* tags = STRING( m_iszTags );
+ CSplitString splitTags( tags, " " );
+ for ( int i=0; i<splitTags.Count(); ++i )
+ {
+ m_tags.CopyAndAddToTail( splitTags[i] );
+ }
+#endif
+
+ SetDisabled( m_bDisabled );
+}
+
+void CCaptureFlag::UpdateOnRemove( void )
+{
+#ifndef GAME_DLL
+ DestroySiren();
+#endif
+
+ // This makes the player stop glowing
+ CTFPlayer *pOwnerPlayer = dynamic_cast< CTFPlayer * >( GetOwnerEntity() );
+ if ( pOwnerPlayer )
+ {
+ pOwnerPlayer->SetItem( NULL );
+ }
+
+ BaseClass::UpdateOnRemove();
+}
+
+#ifdef GAME_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::PlaySound( IRecipientFilter& filter, const char *pszString, int iTeam /*= TEAM_ANY */ )
+{
+ // Note: iTeam parameter is only used for rate-limiting flag sounds based on team, and does not affect
+ // who the sound is targetted at; the filter parameter is the only thing that will affect who hears this.
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ // Don't play bomb announcements unless we're at least 5 seconds into a wave in MVM
+ if ( !( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING && gpGlobals->curtime - TFGameRules()->GetLastRoundStateChangeTime() >= 5.0f ) )
+ {
+ return;
+ }
+
+ // Only play the reset sound in MVM
+ // Other flag announcements are too noisy
+ if ( V_strcmp( pszString, TF_MVM_AD_ENEMY_RETURNED ) == 0 )
+ {
+ EmitSound( filter, entindex(), pszString );
+ }
+ }
+ else if ( TFGameRules()->IsPlayingSpecialDeliveryMode() && ( ( V_strcmp( pszString, TF_RESOURCE_TEAM_DROPPED ) == 0 ) || ( V_strcmp( pszString, TF_RESOURCE_EVENT_TEAM_DROPPED ) == 0 ) ) )
+ {
+ // Rate limit certain flag sounds in Special Delivery
+ if ( iTeam == TEAM_ANY || gpGlobals->curtime >= m_flNextTeamSoundTime[iTeam] )
+ {
+ EmitSound( filter, entindex(), pszString );
+
+ if ( iTeam != TEAM_ANY )
+ {
+ m_flNextTeamSoundTime[iTeam] = gpGlobals->curtime + 20.0f;
+ }
+ }
+ }
+ else
+ {
+ EmitSound( filter, entindex(), pszString );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the return time for first down mode, if enabled and supported, otherwise
+// simply returns the passed in nReturnTime.
+//-----------------------------------------------------------------------------
+int CCaptureFlag::GetReturnTimeShotClockMode(int nStartReturnTime)
+{
+ int nReturnTime = nStartReturnTime;
+
+ // Only enable this for specific modes.
+ if (IsFlagShotClockModePossible())
+ {
+ if (m_bUseShotClockMode)
+ {
+ // When the game is in a standoff (both flags are stolen and poisonous), return when next dropped
+ // This makes it easier to resolve the standoff and continue the game
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() && TFGameRules()->PowerupModeFlagStandoffActive() )
+ {
+ return 0;
+ }
+
+ float flCreditTime = (gpGlobals->curtime - m_flLastPickupTime) * tf_flag_return_time_credit_factor.GetFloat()
+ + m_flLastResetDuration;
+ int nPossibleCreditTime = RoundFloatToInt(flCreditTime);
+ int nActualCreditTime = MAX(0, nPossibleCreditTime);
+ nReturnTime = MIN(nStartReturnTime, nActualCreditTime);
+ }
+ }
+
+ return nReturnTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CCaptureFlag &CCaptureFlag::operator=( const CCaptureFlag& rhs )
+{
+ m_bDisabled = rhs.m_bDisabled;
+ m_nType = rhs.m_nType;
+ m_nReturnTime = rhs.m_nReturnTime;
+ m_nUseTrailEffect = rhs.m_nUseTrailEffect;
+ m_nNeutralType = rhs.m_nNeutralType;
+ m_nScoringType = rhs.m_nScoringType;
+ m_bReturnBetweenWaves = rhs.m_bReturnBetweenWaves;
+ m_bVisibleWhenDisabled = rhs.m_bVisibleWhenDisabled;
+ m_iszModel = rhs.m_iszModel;
+ m_iszHudIcon = rhs.m_iszHudIcon;
+ m_iszPaperEffect = rhs.m_iszPaperEffect;
+ m_iszTrailEffect = rhs.m_iszTrailEffect;
+ m_iszTags = rhs.m_iszTags;
+ m_nSkin = rhs.m_nSkin;
+ m_bUseShotClockMode = rhs.m_bUseShotClockMode;
+
+ ChangeTeam( rhs.GetTeamNumber() );
+
+ return *this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Activate( void )
+{
+ BaseClass::Activate();
+
+ m_iOriginalTeam = GetTeamNumber();
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CCaptureFlag* CCaptureFlag::Create( const Vector& vecOrigin, const char *pszModelName, ETFFlagType type )
+{
+ CCaptureFlag *pFlag = static_cast< CCaptureFlag* >( CBaseEntity::CreateNoSpawn( "item_teamflag", vecOrigin, vec3_angle, NULL ) );
+ pFlag->m_iszModel = MAKE_STRING( pszModelName );
+ pFlag->m_nType = type;
+
+ // don't show trail effect for PD flag
+ if ( type == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ pFlag->m_nUseTrailEffect = FLAG_EFFECTS_NONE;
+ }
+
+ DispatchSpawn( pFlag );
+
+ return pFlag;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset the flag position state.
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Reset( void )
+{
+#ifdef GAME_DLL
+ m_bInstantTrailRemove = true;
+ RemoveFlagTrail();
+
+ if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION && !IsDisabled() )
+ {
+ if ( m_nPointValue > 0 )
+ {
+ // Score points!
+ if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
+ {
+ CTFRobotDestructionLogic::GetRobotDestructionLogic()->ScorePoints( GetTeamNumber()
+ , m_nPointValue.Get()
+ , SCORE_REACTOR_RETURNED
+ , NULL );
+
+ CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
+ m_nPointValue = 0;
+ }
+ }
+
+ // Disable ourselves if our team currently has no points
+ SetDisabled( CTFRobotDestructionLogic::GetRobotDestructionLogic()->GetTargetScore( GetTeamNumber() ) < tf_rd_min_points_to_steal.GetInt() );
+ }
+ else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ // Set the flag position.
+ if ( !m_hInitialParent.Get() )
+ {
+ SetAbsOrigin( m_vecResetPos );
+ SetParent( NULL );
+ }
+ else
+ {
+ SetAbsOrigin( m_hInitialParent->GetAbsOrigin() + m_vecOffset );
+ SetParent( m_hInitialParent.Get() );
+ }
+
+ SetAbsAngles( m_vecResetAng );
+
+ // No longer dropped, if it was.
+ SetFlagStatus( TF_FLAGINFO_HOME );
+ ResetFlagReturnTime();
+ ResetFlagNeutralTime();
+
+ m_hInitialPlayer = NULL;
+
+ m_bAllowOwnerPickup = true;
+ m_hPrevOwner = NULL;
+ m_flTimeToSetPoisonous = 0.f;
+
+ if ( m_nType == TF_FLAGTYPE_INVADE || m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ ChangeTeam( m_iOriginalTeam );
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+ }
+
+ SetMoveType( MOVETYPE_NONE );
+
+ // update the objective resource so clients have the information
+ if ( TFObjectiveResource() )
+ {
+ TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
+ TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
+ TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::ResetMessage( void )
+{
+#ifdef GAME_DLL
+ if ( m_nType == TF_FLAGTYPE_CTF )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam == GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_ENEMY_RETURNED );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_RETURNED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_TEAM_RETURNED );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_RETURNED );
+ }
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
+ if ( event )
+ {
+ event->SetInt( "eventtype", TF_FLAGEVENT_RETURNED );
+ event->SetInt( "priority", 8 );
+ event->SetInt( "team", GetTeamNumber() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Returned sound
+ CPASAttenuationFilter filter( this, TF_CTF_FLAGSPAWN );
+ PlaySound( filter, TF_CTF_FLAGSPAWN );
+ }
+ else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam == GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_AD_TEAM_RETURNED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ PlaySound( filter, TF_MVM_AD_ENEMY_RETURNED );
+ }
+ else
+ {
+ PlaySound( filter, TF_AD_ENEMY_RETURNED );
+ }
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_INVADE )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_FLAG_RETURNED );
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ const char *pszSound = TF_RESOURCE_RETURNED;
+ if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ TFGameRules()->StartDoomsdayTicketsTimer();
+ pszSound = TF_RESOURCE_EVENT_RETURNED;
+ }
+
+ TFGameRules()->BroadcastSound( 255, pszSound );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
+ if ( event )
+ {
+ event->SetInt( "eventtype", TF_FLAGEVENT_RETURNED );
+ event->SetInt( "priority", 8 );
+ event->SetInt( "team", GetTeamNumber() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Returned sound
+ CPASAttenuationFilter filter( this, TF_RESOURCE_FLAGSPAWN );
+ PlaySound( filter, TF_RESOURCE_FLAGSPAWN );
+ }
+ else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam == GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_ENEMY_RETURNED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_TEAM_RETURNED );
+ }
+ }
+ }
+
+ // Output.
+ m_outputOnReturn.FireOutput( this, this );
+
+ DestroyReturnIcon();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::FlagTouch( CBaseEntity *pOther )
+{
+ // Is the flag disabled or stolen already?
+ if ( IsDisabled() || IsStolen() )
+ {
+ return;
+ }
+
+ // The touch from a live player.
+ if ( !pOther->IsPlayer() || !pOther->IsAlive() )
+ {
+ return;
+ }
+
+#ifdef GAME_DLL
+ // Don't let the person who threw this flag pick it up until it hits the ground.
+ // This way we can throw the flag to people, but not touch it as soon as we throw it ourselves
+ if( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pOther && m_bAllowOwnerPickup == false )
+ {
+ return;
+ }
+#endif
+
+ if ( pOther->GetTeamNumber() == GetTeamNumber() )
+ {
+#ifdef GAME_DLL
+ m_OnTouchSameTeam.FireOutput( this, this );
+#endif
+
+ // Does my team own this flag? If so, no touch.
+ if ( m_nType == TF_FLAGTYPE_CTF || m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ if ( !tf_flag_return_on_touch.GetBool() )
+ return;
+
+ if ( IsHome() || IsStolen() )
+ return;
+ }
+ }
+
+ if ( ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND || m_nType == TF_FLAGTYPE_TERRITORY_CONTROL ) &&
+ pOther->GetTeamNumber() != GetTeamNumber() )
+ {
+ return;
+ }
+
+ if ( ( m_nType == TF_FLAGTYPE_INVADE || m_nType == TF_FLAGTYPE_RESOURCE_CONTROL ) && ( GetTeamNumber() != TEAM_UNASSIGNED ) )
+ {
+ if ( pOther->GetTeamNumber() != GetTeamNumber() )
+ {
+ return;
+ }
+ }
+
+ // Can't pickup flags during WaitingForPlayers
+ if ( TFGameRules()->IsInWaitingForPlayers() )
+ return;
+
+ // Get the touching player.
+ CTFPlayer *pPlayer = ToTFPlayer( pOther );
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ if ( !pPlayer->IsAllowedToPickUpFlag() )
+ {
+ return;
+ }
+
+#ifdef GAME_DLL
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ // skip all the restrictions and let bots pick up the flag
+ PickUp( pPlayer, true );
+ return;
+ }
+#endif
+
+ // Is the touching player about to teleport?
+ if ( pPlayer->m_Shared.InCond( TF_COND_SELECTED_TO_TELEPORT ) )
+ return;
+
+ // Don't let invulnerable players pickup flags, except in PD
+ if ( pPlayer->m_Shared.IsInvulnerable() && m_nType != TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ return;
+
+ // Don't let stealthed spies pickup the flag
+ if ( pPlayer->m_Shared.IsStealthed() || pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) || pPlayer->m_Shared.GetPercentInvisible() > 0.25f )
+ return;
+
+ // Don't let phased scouts pickup flags
+ if ( pPlayer->m_Shared.InCond( TF_COND_PHASE ) )
+ return;
+
+ // Don't let players carry multiple flags for user-made maps with >1
+ if ( pPlayer->HasTheFlag() && !( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION || m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION ) && !tf_flag_return_on_touch.GetBool() )
+ return;
+
+#ifdef GAME_DLL
+ if ( PointInRespawnRoom(pPlayer,pPlayer->WorldSpaceCenter()) )
+ return;
+#endif
+
+ if ( IsDropped() && ( pOther->GetTeamNumber() == GetTeamNumber() ) && ( m_nType == TF_FLAGTYPE_CTF ) && tf_flag_return_on_touch.GetBool() )
+ {
+ Reset();
+ ResetMessage();
+#ifdef GAME_DLL
+ CTF_GameStats.Event_PlayerReturnedFlag( pPlayer );
+#endif
+ }
+ else
+ {
+ // Pick up the flag.
+ PickUp( pPlayer, true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::PickUp( CTFPlayer *pPlayer, bool bInvisible )
+{
+ // Is the flag enabled?
+ if ( IsDisabled() )
+ return;
+
+ if ( !TFGameRules()->FlagsMayBeCapped() )
+ return;
+
+ // For maps/scenarios with multiple flags, only allow one flag per player
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ if ( pPlayer->HasTheFlag() )
+ return;
+ }
+
+#ifdef GAME_DLL
+ if ( !m_bAllowOwnerPickup )
+ {
+ if ( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pPlayer )
+ {
+ return;
+ }
+ }
+
+ if ( TFGameRules()->IsMannVsMachineMode() && pPlayer->IsBot() )
+ {
+ CTFBot *pBot = assert_cast< CTFBot* >( pPlayer );
+
+ if ( pBot->HasAttribute( CTFBot::IGNORE_FLAG ) )
+ return;
+
+ pBot->SetFlagTarget( this );
+ }
+
+ if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ if ( pPlayer->HasItem() && ( pPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG ) && pPlayer->GetItem() != this )
+ {
+ CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
+
+ // If the player who touched us is on the other team and is already carrying a flag, add our score
+ // onto the flag that they're carrying
+ CCaptureFlag* pOtherFlag = static_cast< CCaptureFlag * >( pPlayer->GetItem() );
+ pOtherFlag->AddPointValue( m_nPointValue );
+ UTIL_Remove( this );
+ return;
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( pPlayer->HasItem() && ( pPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG ) && pPlayer->GetItem() != this )
+ {
+ // If the player who touched us is already carrying a flag, add our score
+ // onto the flag that they're carrying
+ CCaptureFlag* pOtherFlag = static_cast< CCaptureFlag * >( pPlayer->GetItem() );
+ pOtherFlag->AddPointValue( m_nPointValue );
+ UTIL_Remove( this );
+
+ if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
+ {
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropPickupSound( pPlayer );
+ }
+
+ return;
+ }
+ }
+#endif
+
+ // Call into the base class pickup.
+ BaseClass::PickUp( pPlayer, false );
+
+ pPlayer->TeamFortress_SetSpeed();
+
+#ifdef GAME_DLL
+
+ // Update the parent to set the correct place on the model to attach the flag.
+ int iAttachment = pPlayer->LookupAttachment( "flag" );
+ if( iAttachment > 0 )
+ {
+ SetParent( pPlayer, iAttachment );
+ SetLocalOrigin( vec3_origin );
+ SetLocalAngles( vec3_angle );
+ }
+
+ // Remove the player's disguise if they're a spy, but not in PD
+ if ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY && m_nType != TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ||
+ pPlayer->m_Shared.InCond( TF_COND_DISGUISING ))
+ {
+ pPlayer->m_Shared.RemoveDisguise();
+ }
+ }
+
+ // switch to brighter picked up skin
+ m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
+
+ // Remove the touch function.
+ SetTouch( NULL );
+
+ m_hPrevOwner = pPlayer;
+ m_bAllowOwnerPickup = true;
+
+ if ( m_hInitialPlayer.Get() == NULL )
+ {
+ m_hInitialPlayer = pPlayer;
+ }
+
+ if ( m_nType == TF_FLAGTYPE_CTF )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_ENEMY_STOLEN );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_TAKEN );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_TEAM_STOLEN );
+
+ // exclude the guy who just picked it up
+ filter.RemoveRecipient( pPlayer );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_TAKEN );
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ PlaySound( filter, TF_MVM_AD_ENEMY_STOLEN );
+ }
+ else
+ {
+ PlaySound( filter, TF_AD_ENEMY_STOLEN );
+ }
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_AD_TEAM_STOLEN );
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_INVADE )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_ENEMY_STOLEN );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_TEAM_STOLEN );
+ }
+ }
+
+ // set the flag's team to match the player's team
+ ChangeTeam( pPlayer->GetTeamNumber() );
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+ m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
+ }
+ else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ // In Special delivery we only tell them about the very first flag pick up from neutral
+ if ( GetTeamNumber() == TEAM_UNASSIGNED )
+ {
+ bool bEventMap = TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY );
+ if ( bEventMap )
+ {
+ TFGameRules()->StopDoomsdayTicketsTimer();
+ }
+
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ const char *pszSound = TF_RESOURCE_ENEMY_STOLEN;
+
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ if ( bEventMap )
+ {
+ pszSound = TF_RESOURCE_EVENT_ENEMY_STOLEN;
+ }
+ }
+ else
+ {
+ pszSound = TF_RESOURCE_TEAM_STOLEN;
+ if ( bEventMap )
+ {
+ pszSound = TF_RESOURCE_EVENT_TEAM_STOLEN;
+ }
+ }
+
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, pszSound, iTeam );
+ }
+ }
+
+ // set the flag's team to match the player's team
+ ChangeTeam( pPlayer->GetTeamNumber() );
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+ m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
+ }
+ else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_ENEMY_STOLEN, iTeam );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_TEAM_STOLEN, iTeam );
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
+ {
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropPickupSound( pPlayer );
+ }
+ }
+
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() && m_flTimeToSetPoisonous == 0.f )
+ {
+ // replace 90.f with a convar?
+ m_flTimeToSetPoisonous = gpGlobals->curtime + 90.f;
+ }
+
+ // If the flag was at home, set the initial reset time to the max allowable time, otherwise it's to whatever it was
+ // right now so we can persist that until later.
+ if ( m_nFlagStatus == TF_FLAGINFO_HOME )
+ {
+ m_flLastResetDuration = GetMaxReturnTime();
+ }
+ else
+ {
+ m_flLastResetDuration = m_flResetTime - gpGlobals->curtime;
+ }
+
+ // Remember that this is when the item was picked up.
+ m_flLastPickupTime = gpGlobals->curtime;
+
+ int nOldFlagStatus = m_nFlagStatus;
+ SetFlagStatus( TF_FLAGINFO_STOLEN, pPlayer );
+ ResetFlagReturnTime();
+ ResetFlagNeutralTime();
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
+ if ( event )
+ {
+ event->SetInt( "player", pPlayer->entindex() );
+ event->SetInt( "eventtype", TF_FLAGEVENT_PICKUP );
+ event->SetInt( "priority", 8 );
+ event->SetInt( "home", ( nOldFlagStatus == TF_FLAGINFO_HOME ) ? 1 : 0 );
+ event->SetInt( "team", GetTeamNumber() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGPICKUP );
+
+ // Output.
+ m_outputOnPickUp.FireOutput( this, this );
+
+ switch ( pPlayer->GetTeamNumber() )
+ {
+ case TF_TEAM_RED:
+ m_outputOnPickUpTeam1.FireOutput( this, this );
+ break;
+ case TF_TEAM_BLUE:
+ m_outputOnPickUpTeam2.FireOutput( this, this );
+ break;
+ default:
+ break;
+ }
+
+ DestroyReturnIcon();
+
+ StartFlagTrail();
+
+ HandleFlagPickedUpInDetectionZone( pPlayer );
+
+ // update the objective resource so clients have the information
+ if ( TFObjectiveResource() )
+ {
+ TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
+ TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
+ TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
+ }
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ if ( nOldFlagStatus == TF_FLAGINFO_HOME )
+ {
+ if ( pPlayer->IsMiniBoss() )
+ {
+ TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_HAS_BOMB, TF_TEAM_PVE_DEFENDERS );
+ }
+ else
+ {
+ TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_FIRST_BOMB_PICKUP, TF_TEAM_PVE_DEFENDERS );
+ }
+ }
+ else
+ {
+ if ( pPlayer->IsMiniBoss() )
+ {
+ TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_HAS_BOMB, TF_TEAM_PVE_DEFENDERS );
+ }
+ else
+ {
+ TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_PICKUP, TF_TEAM_PVE_DEFENDERS );
+ }
+ }
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Capture( CTFPlayer *pPlayer, int nCapturePoint )
+{
+ // Is the flag enabled?
+ if ( IsDisabled() )
+ return;
+
+#ifdef GAME_DLL
+
+ if ( m_nType == TF_FLAGTYPE_CTF )
+ {
+ bool bNotify = true;
+
+ // don't play any sounds if this is going to win the round for one of the teams (victory sound will be played instead)
+ if ( tf_flag_caps_per_round.GetInt() > 0 )
+ {
+ int nCaps = TFTeamMgr()->GetFlagCaptures( pPlayer->GetTeamNumber() );
+
+ if ( ( nCaps >= 0 ) && ( tf_flag_caps_per_round.GetInt() - nCaps <= 1 ) )
+ {
+ // this cap is going to win, so don't play a sound
+ bNotify = false;
+ }
+ }
+
+ if ( bNotify )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_ENEMY_CAPTURED );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_CAPTURED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+
+ if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
+ {
+ PlaySound( filter, TF_RUNE_INTEL_CAPTURED );
+ }
+ else
+ {
+ PlaySound( filter, TF_CTF_TEAM_CAPTURED );
+ }
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_CAPTURED );
+ }
+ }
+
+ if ( TFGameRules() )
+ {
+ TFGameRules()->HandleCTFCaptureBonus( pPlayer->GetTeamNumber() );
+ }
+ }
+
+ // Reward the player
+ CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
+
+ int nAmount = TFGameRules()->CalculateCurrencyAmount_ByType( TF_CURRENCY_CAPTURED_OBJECTIVE );
+#ifdef STAGING_ONLY
+ if ( TFGameRules()->GameModeUsesExperience() )
+ {
+ pPlayer->AddExperiencePoints( nAmount );
+ }
+#endif // STAGING_ONLY
+ TFGameRules()->DistributeCurrencyAmount( nAmount, pPlayer );
+
+ // if someone else stole the flag, give them credit, too
+ if ( m_hInitialPlayer.Get() && m_hInitialPlayer.Get() != pPlayer )
+ {
+ CTF_GameStats.Event_PlayerCapturedPoint( ToTFPlayer( m_hInitialPlayer.Get() ) );
+ m_hInitialPlayer = NULL;
+ }
+
+ // Reward the team
+ if ( tf_flag_caps_per_round.GetInt() > 0 )
+ {
+ TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
+ }
+ else
+ {
+ TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_CTF_CAPTURED_TEAM_SCORE );
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
+ {
+ char szNumber[64];
+ Q_snprintf( szNumber, sizeof(szNumber), "%d", nCapturePoint );
+
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ PlaySound( filter, TF_MVM_AD_ENEMY_CAPTURED );
+ }
+ else
+ {
+ PlaySound( filter, TF_AD_ENEMY_CAPTURED );
+ }
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_AD_TEAM_CAPTURED );
+ }
+ }
+
+ // Capture sound
+ CBroadcastRecipientFilter filter;
+ PlaySound( filter, TF_AD_CAPTURED_SOUND );
+
+ // Reward the player
+ CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
+
+ // TFTODO:: Reward the team
+ }
+ else if ( m_nType == TF_FLAGTYPE_INVADE )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_ENEMY_CAPTURED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_TEAM_CAPTURED );
+ }
+ }
+
+ // Reward the player
+ CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
+
+ // Reward the team
+ if ( m_nScoringType == INVADE_SCORING_TEAM_CAPTURE_COUNT )
+ {
+ if ( tf_flag_caps_per_round.GetInt() > 0 )
+ {
+ TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
+ }
+ else
+ {
+ TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_INVADE_CAPTURED_TEAM_SCORE );
+ }
+ }
+ else
+ {
+ TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_INVADE_CAPTURED_TEAM_SCORE );
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ TFGameRules()->BroadcastSound( 255, ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? TF_RESOURCE_EVENT_RED_CAPPED : TF_RESOURCE_EVENT_BLUE_CAPPED );
+ }
+ else
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ const char *pszSound = TF_RESOURCE_ENEMY_CAPTURED;
+ if ( iTeam == pPlayer->GetTeamNumber() )
+ {
+ pszSound = TF_RESOURCE_TEAM_CAPTURED;
+ }
+
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, pszSound, iTeam );
+ }
+ }
+
+ // Reward the player
+ CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
+
+ // if someone else stole the flag, give them credit, too
+ if ( m_hInitialPlayer.Get() && m_hInitialPlayer.Get() != pPlayer )
+ {
+ CTF_GameStats.Event_PlayerCapturedPoint( ToTFPlayer( m_hInitialPlayer.Get() ) );
+ m_hInitialPlayer = NULL;
+ }
+
+ // Reward the team
+ if ( tf_flag_caps_per_round.GetInt() > 0 )
+ {
+ TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
+ }
+ else
+ {
+ TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_RESOURCE_CAPTURED_TEAM_SCORE );
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_ENEMY_CAPTURED, iTeam );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_TEAM_CAPTURED, iTeam );
+ }
+ }
+
+ // Score points!
+ if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
+ {
+ CTFRobotDestructionLogic::GetRobotDestructionLogic()->ScorePoints( pPlayer->GetTeamNumber()
+ , m_nPointValue.Get()
+ , SCORE_REACTOR_CAPTURED
+ , pPlayer );
+ CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
+ m_nPointValue = 0;
+ }
+ }
+
+ if ( IsPoisonous() )
+ {
+ pPlayer->m_Shared.RemoveCond( TF_COND_MARKEDFORDEATH );
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
+ if ( event )
+ {
+ event->SetInt( "player", pPlayer->entindex() );
+ event->SetInt( "eventtype", TF_FLAGEVENT_CAPTURE );
+ event->SetInt( "priority", 9 );
+ event->SetInt( "team", GetTeamNumber() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ SetFlagStatus( TF_FLAGINFO_HOME );
+ ResetFlagReturnTime();
+ ResetFlagNeutralTime();
+
+ m_bInstantTrailRemove = true;
+ RemoveFlagTrail();
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+
+ HandleFlagCapturedInDetectionZone( pPlayer );
+ HandleFlagDroppedInDetectionZone( pPlayer );
+
+ // Reset the flag.
+ BaseClass::Drop( pPlayer, true );
+
+ Reset();
+
+ pPlayer->TeamFortress_SetSpeed();
+
+ if ( !TFGameRules() || !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGCAPTURED );
+ }
+
+ // Outputs
+ m_outputOnCapture.FireOutput( this, this );
+
+ switch ( pPlayer->GetTeamNumber() )
+ {
+ case TF_TEAM_RED:
+ m_OnCapTeam1.FireOutput( this, this );
+ break;
+ case TF_TEAM_BLUE:
+ m_OnCapTeam2.FireOutput( this, this );
+ break;
+ default:
+ break;
+ }
+
+ m_bCaptured = true;
+ SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
+
+ if ( TFGameRules()->InStalemate() )
+ {
+ // whoever capped the flag is the winner, give them enough caps to win
+ CTFTeam *pTeam = pPlayer->GetTFTeam();
+ if ( !pTeam )
+ return;
+
+ // if we still need more caps to trigger a win, give them to us
+ if ( pTeam->GetFlagCaptures() < tf_flag_caps_per_round.GetInt() )
+ {
+ pTeam->SetFlagCaptures( tf_flag_caps_per_round.GetInt() );
+ }
+ }
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A player drops the flag.
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown /*= false*/, bool bMessage /*= true*/ )
+{
+ // Is the flag enabled?
+ if ( IsDisabled() )
+ return;
+
+ // Call into the base class drop.
+ BaseClass::Drop( pPlayer, bVisible );
+
+ pPlayer->TeamFortress_SetSpeed();
+
+#ifdef GAME_DLL
+
+ if ( bThrown )
+ {
+ m_bAllowOwnerPickup = false;
+ m_flOwnerPickupTime = gpGlobals->curtime + TF_FLAG_OWNER_PICKUP_TIME;
+ }
+
+ // Drop from the player's center so we can guarantee that it is in a valid spot
+ Vector vecStart = pPlayer->WorldSpaceCenter();
+ Vector vecEnd = vecStart;
+ vecEnd.z -= 8000.0f;
+ trace_t trace;
+ UTIL_TraceHull( vecStart, vecEnd, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &trace );
+
+ if ( trace.startsolid )
+ {
+ DevWarning( "Dropped flag trace started solid!\nWiggle around each axis to find a safer fit!\n" );
+
+ const float fMultipliers[ 3 ] = { 0.0f, 1.0f, -1.0f };
+
+ // Wiggle it around on each axis to find a safe place
+ for ( int z = 0; z < ARRAYSIZE( fMultipliers ) && trace.startsolid; z++ )
+ {
+ for ( int y = 0; y < ARRAYSIZE( fMultipliers ) && trace.startsolid; y++ )
+ {
+ for ( int x = 0; x < ARRAYSIZE( fMultipliers ) && trace.startsolid; x++ )
+ {
+ vecStart = pPlayer->WorldSpaceCenter();
+ vecStart += Vector( fMultipliers[ x ] * 10.0f, fMultipliers[ y ] * 10.0f, fMultipliers[ z ] * 10.0f );
+
+ vecEnd = vecStart;
+ vecEnd.z -= 8000.0f;
+ UTIL_TraceHull( vecStart, vecEnd, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &trace );
+ }
+ }
+ }
+ }
+
+ if ( trace.startsolid )
+ {
+ // Couldn't find a good spot... just leave it in the center of where the player died
+ AssertMsg( 0, "Couldn't find a safe place to drop the flag!\n" );
+ DevWarning( "Couldn't find a safe place to drop the flag!\nDropping at the player's center!\n" );
+
+ SetAbsOrigin( pPlayer->WorldSpaceCenter() );
+ }
+ else
+ {
+ // Found a good spot for it
+ SetAbsOrigin( trace.endpos );
+
+ // If it lands on an elevator, parent it to the elevator
+ if ( trace.m_pEnt && trace.m_pEnt->GetMoveType() == MOVETYPE_PUSH )
+ {
+ SetParent( trace.m_pEnt );
+ }
+ }
+
+ // ensure the bomb drops somewhere the bots can reach it in MvM mode
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ Vector bombPos = GetAbsOrigin();
+
+ CTFNavArea *bombArea = (CTFNavArea *)TheNavMesh->GetNavArea( bombPos, 99999.9f );
+
+ bool isBombInBadPlace = false;
+
+ if ( bombArea )
+ {
+ if ( bombArea->HasAttributeTF( TF_NAV_BOMB_CAN_DROP_HERE ) )
+ {
+ float height = bombArea->GetZ( bombPos );
+
+ if ( height > HalfHumanHeight )
+ {
+ isBombInBadPlace = true;
+ }
+ }
+ else
+ {
+ // Bomb not allowed in this nav area
+ isBombInBadPlace = true;
+ }
+ }
+ else
+ {
+ // Bomb is off the mesh
+ isBombInBadPlace = true;
+ }
+
+ if ( isBombInBadPlace )
+ {
+ // the bomb has dropped in an invalid spot - move it to a nearby valid area
+ const float searchRange = 500.0f;
+
+ Extent nearExtent;
+ nearExtent.lo = bombPos;
+ nearExtent.lo.x -= searchRange;
+ nearExtent.lo.y -= searchRange;
+ nearExtent.lo.z = MIN_COORD_FLOAT; // make sure we catch all areas under flag, even it is way up in the air for some reason
+
+ nearExtent.hi = bombPos;
+ nearExtent.hi.x += searchRange;
+ nearExtent.hi.y += searchRange;
+ nearExtent.hi.z += searchRange;
+
+ CUtlVector< CTFNavArea * > nearAreaVector;
+
+ TheNavMesh->CollectAreasOverlappingExtent< CTFNavArea >( nearExtent, &nearAreaVector );
+
+ CTFNavArea *nearValidArea = NULL;
+ float nearRangeSq = FLT_MAX;
+ Vector nearSpot;
+
+ for( int i=0; i<nearAreaVector.Count(); ++i )
+ {
+ CTFNavArea *area = nearAreaVector[i];
+
+ if ( area->HasAttributeTF( TF_NAV_BOMB_CAN_DROP_HERE ) )
+ {
+ area->GetClosestPointOnArea( bombPos, &nearSpot );
+
+ float rangeSq = ( nearSpot - bombPos ).LengthSqr();
+
+ if ( rangeSq < nearRangeSq )
+ {
+ nearRangeSq = rangeSq;
+ nearValidArea = area;
+ }
+ }
+ }
+
+
+ if ( nearValidArea )
+ {
+ nearValidArea->GetClosestPointOnArea( bombPos, &bombPos );
+ bombPos.z += 5.0f;
+
+ SetAbsOrigin( bombPos );
+ }
+ }
+ }
+
+ if ( m_nType == TF_FLAGTYPE_CTF )
+ {
+ if ( bMessage )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_ENEMY_DROPPED );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_DROPPED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_CTF_TEAM_DROPPED );
+
+ TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_DROPPED );
+ }
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_INVADE )
+ {
+ if ( bMessage )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_ENEMY_DROPPED );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_INVADE_TEAM_DROPPED );
+ }
+ }
+ }
+
+ if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_HALF )
+ {
+ SetFlagNeutralIn( (float)GetMaxReturnTime() / 2.0 );
+ }
+ else if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_DEFAULT )
+ {
+ // if our return time is less than the neutral time, we don't need a neutral time
+ if ( TF_INVADE_NEUTRAL_TIME < GetMaxReturnTime() )
+ {
+ SetFlagNeutralIn( TF_INVADE_NEUTRAL_TIME );
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
+ {
+ if ( bMessage )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ if ( iTeam != pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+
+ if ( TFGameRules()->IsMannVsMachineMode() )
+ {
+ PlaySound( filter, TF_MVM_AD_ENEMY_DROPPED );
+ }
+ else
+ {
+ PlaySound( filter, TF_AD_ENEMY_DROPPED );
+ }
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_AD_TEAM_DROPPED );
+ }
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ if ( bMessage )
+ {
+ const char *pszSound = TF_RESOURCE_TEAM_DROPPED;
+ if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ pszSound = TF_RESOURCE_EVENT_TEAM_DROPPED;
+ }
+
+ // We only care about our own team dropping it in Special Delivery
+ int iTeam = pPlayer->GetTeamNumber();
+
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, pszSound, iTeam );
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ // If a player dropped this flag but the flag has less than the min to steal
+ // we just return the flag rather than have it exist on the ground
+ if ( GetPointValue() < tf_rd_min_points_to_steal.GetInt() )
+ {
+ ResetFlag();
+ return;
+ }
+ else if ( bMessage )
+ {
+ for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
+ {
+ // We only care about our own team dropping it in Special Delivery
+ if ( iTeam == pPlayer->GetTeamNumber() )
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_TEAM_DROPPED, iTeam );
+ }
+ else
+ {
+ CTeamRecipientFilter filter( iTeam, true );
+ PlaySound( filter, TF_RD_ENEMY_DROPPED, iTeam );
+ }
+ }
+ }
+ }
+ else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
+ {
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
+ CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropDropSound( pPlayer );
+ }
+ }
+
+ if ( IsPoisonous() )
+ {
+ pPlayer->m_Shared.RemoveCond( TF_COND_MARKEDFORDEATH );
+ }
+
+ m_nSkin = m_nSkin - TF_FLAG_NUMBEROFSKINS;
+
+ RemoveFlagTrail();
+
+ int nMaxReturnTime = GetMaxReturnTime();
+ SetFlagReturnIn( GetReturnTime( nMaxReturnTime ), nMaxReturnTime );
+
+ // Reset the flag's angles.
+ SetAbsAngles( m_vecResetAng );
+
+ // Reset the touch function.
+ SetTouch( &CCaptureFlag::FlagTouch );
+
+ SetFlagStatus( TF_FLAGINFO_DROPPED );
+
+ // Output.
+ m_outputOnDrop.FireOutput( this, this );
+
+ if ( !TFGameRules()->IsMannVsMachineMode() || ( GetMaxReturnTime() < 600 ) )
+ {
+ CreateReturnIcon();
+ }
+
+ // did we get dropped in a func_respawnflag zone?
+ if ( PointInRespawnFlagZone( GetAbsOrigin() ) == true )
+ {
+ Reset();
+ ResetMessage();
+ }
+
+ HandleFlagDroppedInDetectionZone( pPlayer );
+
+ // update the objective resource so clients have the information
+ if ( TFObjectiveResource() )
+ {
+ TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
+ TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
+ TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
+ }
+
+ if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
+ {
+ TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_DROPPED, TF_TEAM_PVE_DEFENDERS );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::IsDropped( void )
+{
+ return ( m_nFlagStatus == TF_FLAGINFO_DROPPED );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::IsHome( void )
+{
+ return ( m_nFlagStatus == TF_FLAGINFO_HOME );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::IsStolen( void )
+{
+ return ( m_nFlagStatus == TF_FLAGINFO_STOLEN );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCaptureFlag::IsDisabled( void ) const
+{
+ return m_bDisabled;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::SetDisabled( bool bDisabled )
+{
+ m_bDisabled = bDisabled;
+
+ if ( bDisabled )
+ {
+ if ( m_bVisibleWhenDisabled )
+ {
+ SetRenderMode( kRenderTransAlpha );
+ SetRenderColorA( 180 );
+ RemoveEffects( EF_NODRAW );
+ }
+ else
+ {
+ AddEffects( EF_NODRAW );
+ }
+
+ SetTouch( NULL );
+ SetThink( NULL );
+ }
+ else
+ {
+ RemoveEffects( EF_NODRAW );
+ SetRenderMode( kRenderNormal );
+ SetRenderColorA( 255 );
+
+ // The flag in RD is not actually touched by players when it's home
+ SetTouch( &CCaptureFlag::FlagTouch );
+
+
+ SetThink( &CCaptureFlag::Think );
+ SetNextThink( gpGlobals->curtime );
+
+#ifdef GAME_DLL
+ if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) && ( GetTeamNumber() == TEAM_UNASSIGNED ) )
+ {
+ TFGameRules()->StartDoomsdayTicketsTimer();
+ }
+#endif
+ }
+
+#ifdef CLIENT_DLL
+ UpdateGlowEffect();
+#endif
+}
+
+void CCaptureFlag::SetVisibleWhenDisabled( bool bVisible )
+{
+ m_bVisibleWhenDisabled = bVisible;
+ SetDisabled( IsDisabled() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the flag status
+//-----------------------------------------------------------------------------
+void CCaptureFlag::SetFlagStatus( int iStatus, CBasePlayer *pNewOwner /*= NULL*/ )
+{
+#ifdef GAME_DLL
+ MDLCACHE_CRITICAL_SECTION();
+#endif
+
+ if ( m_nFlagStatus != iStatus )
+ {
+ m_nFlagStatus = iStatus;
+
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
+ if ( pEvent )
+ {
+#ifdef GAME_DLL
+ pEvent->SetInt( "userid", pNewOwner ? pNewOwner->GetUserID() : -1 );
+ pEvent->SetInt( "entindex", entindex() );
+#endif
+ gameeventmanager->FireEvent( pEvent );
+ }
+ }
+
+#ifdef CLIENT_DLL
+ UpdateGlowEffect();
+#endif
+
+#ifdef GAME_DLL
+ switch ( m_nFlagStatus )
+ {
+ case TF_FLAGINFO_HOME:
+ case TF_FLAGINFO_DROPPED:
+ ResetSequence( LookupSequence("spin") ); // set spin animation if it's not being held
+ break;
+ case TF_FLAGINFO_STOLEN:
+ ResetSequence( LookupSequence("idle") ); // set idle animation if it is being held
+ break;
+ default:
+ AssertOnce( false ); // invalid stats
+ break;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------------------------
+// GAME DLL Functions
+//-----------------------------------------------------------------------------------------------
+#ifdef GAME_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::Think( void )
+{
+ // Is the flag enabled?
+ if ( IsDisabled() )
+ return;
+
+ if ( !TFGameRules()->FlagsMayBeCapped() )
+ {
+ SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
+ return;
+ }
+
+ if ( m_bCaptured )
+ {
+ m_bCaptured = false;
+ SetTouch( &CCaptureFlag::FlagTouch );
+ }
+
+ if ( IsDropped() )
+ {
+ if ( !m_bAllowOwnerPickup )
+ {
+ if ( m_flOwnerPickupTime && gpGlobals->curtime > m_flOwnerPickupTime )
+ {
+ m_bAllowOwnerPickup = true;
+ }
+ }
+
+ if ( TFGameRules()->IsMannVsMachineMode() && m_bReturnBetweenWaves )
+ {
+ if ( TFGameRules()->InSetup() || ( TFObjectiveResource() && TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() ) )
+ {
+ Reset();
+ }
+ else if ( g_pPopulationManager && g_pPopulationManager->IsInEndlessWaves() && g_pPopulationManager->EndlessShouldResetFlag() )
+ {
+ Reset();
+ g_pPopulationManager->EndlessFlagHasReset();
+ ResetMessage();
+ }
+ }
+
+ if ( m_nType == TF_FLAGTYPE_INVADE )
+ {
+ if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
+ {
+ Reset();
+ ResetMessage();
+ }
+ else if ( m_flNeutralTime && gpGlobals->curtime > m_flNeutralTime )
+ {
+ // reset the team to the original team setting (when it spawned)
+ ChangeTeam( m_iOriginalTeam );
+ m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
+
+ ResetFlagNeutralTime();
+ }
+ }
+ else
+ {
+ if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
+ {
+ Reset();
+ ResetMessage();
+ }
+ }
+ }
+ else if ( IsStolen() && m_hPrevOwner )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( m_hPrevOwner );
+ if ( pPlayer )
+ {
+ pPlayer->SetLastObjectiveTime( gpGlobals->curtime );
+ }
+ }
+
+ if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
+ {
+ DestroyReturnIcon();
+ }
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
+ if ( pPlayer )
+ {
+ bool bRunning;
+ float flSpeed = pPlayer->MaxSpeed();
+ flSpeed *= flSpeed;
+ if ( pPlayer->GetAbsVelocity().LengthSqr() >= (flSpeed* 0.1f) )
+ {
+ bRunning = true;
+ }
+ else
+ {
+ bRunning = false;
+ }
+
+ if ( !bRunning && m_pFlagTrail )
+ {
+ RemoveFlagTrail();
+ }
+ else if ( bRunning && !m_pFlagTrail )
+ {
+ StartFlagTrail();
+ }
+ }
+
+ if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
+ {
+ if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
+ {
+ if ( TFGameRules()->DoomsdayTicketTimerElapsed() )
+ {
+ if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
+ {
+ // we've started playing a minigame so just cancel the timer
+ TFGameRules()->StopDoomsdayTicketsTimer();
+ }
+ else
+ {
+ TFGameRules()->StartDoomsdayTicketsTimer(); // start the timer again
+ TFGameRules()->BroadcastSound( 255, TF_RESOURCE_EVENT_NAGS );
+ }
+ }
+ }
+ }
+
+ if ( IsStolen() && TFGameRules() && TFGameRules()->IsPowerupMode() && IsPoisonous() && !pPlayer->m_Shared.InCond( TF_COND_MARKEDFORDEATH ) )
+ {
+ pPlayer->m_Shared.AddCond( TF_COND_MARKEDFORDEATH );
+ }
+
+ SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
+}
+
+void CCaptureFlag::CreateReturnIcon( void )
+{
+ if ( m_hReturnIcon.Get() )
+ return;
+
+ CBaseEntity *pReturnIcon = CBaseEntity::Create( "item_teamflag_return_icon", GetAbsOrigin() + Vector(0,0,cl_flag_return_height.GetFloat()), vec3_angle, this );
+ if ( pReturnIcon )
+ {
+ m_hReturnIcon = pReturnIcon;
+ m_hReturnIcon->SetParent( this );
+ }
+}
+
+void CCaptureFlag::DestroyReturnIcon( void )
+{
+ if ( !m_hReturnIcon.Get() )
+ return;
+
+ UTIL_Remove( m_hReturnIcon );
+ m_hReturnIcon = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputEnable( inputdata_t &inputdata )
+{
+ SetDisabled( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputDisable( inputdata_t &inputdata )
+{
+ SetDisabled( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputRoundActivate( inputdata_t &inputdata )
+{
+ CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
+
+ // If the player has a capture flag, drop it.
+ if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
+ {
+ Drop( pPlayer, true, false, false );
+ }
+
+ Reset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputForceDrop( inputdata_t &inputdata )
+{
+ CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
+
+ // If the player has a capture flag, drop it.
+ if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
+ {
+ pPlayer->DropFlag();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InternalForceReset( bool bSilent /* = false */ )
+{
+ if ( IsHome() )
+ return;
+
+ CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
+
+ // If the player has a capture flag, drop it.
+ if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
+ {
+ pPlayer->DropFlag( bSilent );
+ }
+
+ if ( !bSilent )
+ {
+ ResetFlag();
+ }
+ else
+ {
+ Reset();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputForceReset( inputdata_t &inputdata )
+{
+ InternalForceReset();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputForceResetSilent( inputdata_t &inputdata )
+{
+ InternalForceReset( true );
+}
+
+void CCaptureFlag::InputForceResetAndDisableSilent( inputdata_t &inputdata )
+{
+ InternalForceReset( true );
+ SetDisabled( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::InputSetReturnTime( inputdata_t &inputdata )
+{
+ int nReturnTime = inputdata.value.Int();
+ m_nReturnTime = ( nReturnTime >= 0 ) ? nReturnTime : 0;
+
+ if ( IsDropped() )
+ {
+ // do we currently have a neutral time?
+ if ( m_flNeutralTime > 0 )
+ {
+ if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_HALF )
+ {
+ SetFlagNeutralIn( (float)m_nReturnTime / 2.0 );
+ }
+ else if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_DEFAULT )
+ {
+ // if our return time is less than the neutral time, we don't need a neutral time
+ if ( TF_INVADE_NEUTRAL_TIME < m_nReturnTime )
+ {
+ SetFlagNeutralIn( TF_INVADE_NEUTRAL_TIME );
+ }
+ }
+ }
+
+ SetFlagReturnIn( m_nReturnTime );
+ }
+}
+
+void CCaptureFlag::InputShowTimer( inputdata_t &inputdata )
+{
+ int nReturnTime = inputdata.value.Int();
+ m_nReturnTime = ( nReturnTime >= 0 ) ? nReturnTime : 0;
+
+ SetFlagReturnIn( m_nReturnTime );
+
+ CreateReturnIcon();
+}
+
+void CCaptureFlag::InputForceGlowDisabled( inputdata_t &inputdata )
+{
+ int nState = inputdata.value.Int();
+ SetGlowEnabled( nState == 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Always transmitted to clients
+//-----------------------------------------------------------------------------
+int CCaptureFlag::UpdateTransmitState()
+{
+ // ALWAYS transmit to all clients.
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+
+#else
+
+float CCaptureFlag::GetReturnProgress()
+{
+ float flEventTime = MAX( m_flResetTime.m_Value, m_flNeutralTime.m_Value );
+
+ return ( 1.0 - ( ( flEventTime - gpGlobals->curtime ) / m_flMaxResetTime ) );
+}
+
+
+void CCaptureFlag::Simulate( void )
+{
+ BaseClass::Simulate();
+
+ ManageTrailEffects();
+
+ C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
+ if ( m_hPrevOwner && m_hPrevOwner->IsPlayer() && pLocalPlayer && pLocalPlayer->m_Shared.IsFullyInvisible() && !IsEffectActive( EF_NODRAW ) )
+ {
+ C_TFPlayer *pTFOwner = ToTFPlayer( m_hPrevOwner );
+ if ( pTFOwner && pTFOwner != pLocalPlayer )
+ {
+ AddEffects( EF_NODRAW );
+ }
+ }
+ else if ( IsEffectActive( EF_NODRAW ) && ( IsStolen() || IsDropped() ) )
+ {
+ RemoveEffects( EF_NODRAW );
+ }
+}
+
+void CCaptureFlag::ManageTrailEffects( void )
+{
+ if ( ( m_nUseTrailEffect == FLAG_EFFECTS_NONE ) || ( m_nUseTrailEffect == FLAG_EFFECTS_COLORONLY ) )
+ return;
+
+ if ( m_nFlagStatus == TF_FLAGINFO_STOLEN )
+ {
+ if ( GetPrevOwner() )
+ {
+ CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
+
+ if ( pPlayer )
+ {
+ if ( pPlayer->GetAbsVelocity().Length() >= pPlayer->MaxSpeed() * 0.2f )
+ {
+ if ( m_pPaperTrailEffect == NULL )
+ {
+ if ( !( TFGameRules() && TFGameRules()->IsPVEModeActive() ) )
+ {
+ m_pPaperTrailEffect = ParticleProp()->Create( GetPaperEffect(), PATTACH_ABSORIGIN_FOLLOW );
+ }
+ }
+ }
+ else
+ {
+ if ( m_pPaperTrailEffect )
+ {
+ ParticleProp()->StopEmission( m_pPaperTrailEffect );
+ m_pPaperTrailEffect = NULL;
+ }
+ }
+ }
+ }
+
+ }
+
+ else
+ {
+ if ( m_pPaperTrailEffect )
+ {
+ ParticleProp()->StopEmission( m_pPaperTrailEffect );
+ m_pPaperTrailEffect = NULL;
+ }
+ }
+}
+
+
+#endif
+
+
+LINK_ENTITY_TO_CLASS( item_teamflag_return_icon, CCaptureFlagReturnIcon );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlagReturnIcon, DT_CaptureFlagReturnIcon )
+
+BEGIN_NETWORK_TABLE( CCaptureFlagReturnIcon, DT_CaptureFlagReturnIcon )
+END_NETWORK_TABLE()
+
+CCaptureFlagReturnIcon::CCaptureFlagReturnIcon()
+{
+#ifdef CLIENT_DLL
+ m_pReturnProgressMaterial_Empty = NULL;
+ m_pReturnProgressMaterial_Full = NULL;
+#endif
+}
+
+#ifdef GAME_DLL
+
+void CCaptureFlagReturnIcon::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ UTIL_SetSize( this, Vector(-8,-8,-8), Vector(8,8,8) );
+
+ CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) );
+}
+
+int CCaptureFlagReturnIcon::UpdateTransmitState( void )
+{
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start the flag trail
+//-----------------------------------------------------------------------------
+void CCaptureFlag::StartFlagTrail( void )
+{
+ if ( ( m_nUseTrailEffect == FLAG_EFFECTS_NONE ) || ( m_nUseTrailEffect == FLAG_EFFECTS_PAPERONLY ) )
+ return;
+
+ if ( m_pFlagTrail )
+ return;
+
+ CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
+
+ if ( pPlayer )
+ {
+ if ( !m_pFlagTrail )
+ {
+ char szTrailTeamName[ MAX_PATH ];
+ GetTrailEffect( pPlayer->GetTeamNumber(), szTrailTeamName, sizeof( szTrailTeamName ) );
+
+ CSpriteTrail *pTempTrail = NULL;
+
+ pTempTrail = CSpriteTrail::SpriteTrailCreate( szTrailTeamName, GetAbsOrigin(), true );
+ pTempTrail->SetTransmit( false );
+ pTempTrail->FollowEntity( this );
+ pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, TF_FLAG_TRAIL_ALPHA, kRenderFxNone );
+ pTempTrail->SetStartWidth( 32 );
+ pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
+ pTempTrail->SetLifeTime( 0.70 );
+ pTempTrail->TurnOn();
+ pTempTrail->SetAttachment( this, 0 );
+ m_pFlagTrail = pTempTrail;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fade and kill the trail
+//-----------------------------------------------------------------------------
+void CCaptureFlag::RemoveFlagTrail( void )
+{
+ if ( !m_pFlagTrail )
+ return;
+
+ if (m_pFlagTrail)
+ {
+ if (m_flFlagTrailLife <= 0 || m_bInstantTrailRemove == true )
+ {
+ UTIL_Remove( m_pFlagTrail);
+ m_flFlagTrailLife = 1.0f;
+ }
+ else
+ {
+ float fAlpha = TF_FLAG_TRAIL_ALPHA * m_flFlagTrailLife;
+
+ CSpriteTrail *pTempTrail = dynamic_cast< CSpriteTrail*>( m_pFlagTrail.Get() );
+
+ if ( pTempTrail )
+ {
+ pTempTrail->SetBrightness( int(fAlpha) );
+ }
+
+ m_flFlagTrailLife = m_flFlagTrailLife - 0.1f;
+ SetContextThink( &CCaptureFlag::RemoveFlagTrail, gpGlobals->curtime + 0.05, "FadeFlagTrail");
+ }
+ }
+ m_bInstantTrailRemove = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::AddFollower( CTFBot* pBot )
+{
+ if ( !m_followers.HasElement( pBot ) )
+ {
+ m_followers.AddToTail( pBot );
+ for ( int i=0; i<m_tags.Count(); ++i )
+ {
+ pBot->AddTag( m_tags[i] );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::RemoveFollower( CTFBot* pBot )
+{
+ int index = m_followers.Find( pBot );
+ if ( index != m_followers.InvalidIndex() )
+ {
+ m_followers.Remove( index );
+ for ( int i=0; i<m_tags.Count(); ++i )
+ {
+ pBot->RemoveTag( m_tags[i] );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CCaptureFlag::GetReturnTime( int nMaxReturnTime )
+{
+ return GetReturnTimeShotClockMode( nMaxReturnTime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CCaptureFlag::GetMaxReturnTime( void )
+{
+ int nReturnTime = m_nReturnTime;
+
+#ifdef STAGING_ONLY
+ if (tf_flag_return_time_override.GetInt() > 0)
+ {
+ nReturnTime = tf_flag_return_time_override.GetInt();
+ }
+#endif // STAGING_ONLY
+
+ if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
+ {
+ int nMaxPoints = 300;
+ if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
+ {
+ nMaxPoints = CTFRobotDestructionLogic::GetRobotDestructionLogic()->GetMaxPoints();
+ }
+ const int nMaxReturnTimePoints = nMaxPoints / 3;
+ nReturnTime = RemapValClamped(m_nPointValue, 0.f, nMaxReturnTimePoints, tf_rd_return_min_time.GetFloat(), tf_rd_return_max_time.GetFloat());
+ }
+ else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
+ {
+ nReturnTime = CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetFlagResetDelay();
+ }
+ }
+
+ return nReturnTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlag::AddPointValue( int nPoints )
+{
+ m_nPointValue += nPoints;
+
+ if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION || m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
+ {
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "userid", m_hPrevOwner && m_hPrevOwner->IsPlayer() ? ToBasePlayer( m_hPrevOwner )->GetUserID() : -1 );
+ pEvent->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( pEvent );
+
+ // The return time is determined by how many points are in the flag, so update that.
+ m_flLastResetDuration = GetMaxReturnTime();
+ }
+
+ if ( nPoints > 0 )
+ {
+ pEvent = gameeventmanager->CreateEvent( "teamplay_flag_event" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "player", m_hPrevOwner && m_hPrevOwner->IsPlayer() ? ToBasePlayer( m_hPrevOwner )->entindex() : -1 );
+ pEvent->SetInt( "eventtype", TF_FLAGEVENT_PICKUP );
+ gameeventmanager->FireEvent( pEvent );
+ }
+ }
+ }
+}
+#endif // GAME_DLL
+
+#ifdef CLIENT_DLL
+
+typedef struct
+{
+ float maxProgress;
+
+ float vert1x;
+ float vert1y;
+ float vert2x;
+ float vert2y;
+
+ int swipe_dir_x;
+ int swipe_dir_y;
+} progress_segment_t;
+
+
+// This defines the properties of the 8 circle segments
+// in the circular progress bar.
+progress_segment_t Segments[8] =
+{
+ { 0.125, 0.5, 0.0, 1.0, 0.0, 1, 0 },
+ { 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 },
+ { 0.375, 1.0, 0.5, 1.0, 1.0, 0, 1 },
+ { 0.50, 1.0, 1.0, 0.5, 1.0, -1, 0 },
+ { 0.625, 0.5, 1.0, 0.0, 1.0, -1, 0 },
+ { 0.75, 0.0, 1.0, 0.0, 0.5, 0, -1 },
+ { 0.875, 0.0, 0.5, 0.0, 0.0, 0, -1 },
+ { 1.0, 0.0, 0.0, 0.5, 0.0, 1, 0 },
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+RenderGroup_t CCaptureFlagReturnIcon::GetRenderGroup( void )
+{
+ return RENDER_GROUP_TRANSLUCENT_ENTITY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCaptureFlagReturnIcon::GetRenderBounds( Vector& theMins, Vector& theMaxs )
+{
+ theMins.Init( -20, -20, -20 );
+ theMaxs.Init( 20, 20, 20 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CCaptureFlagReturnIcon::DrawModel( int flags )
+{
+ int nRetVal = BaseClass::DrawModel( flags );
+
+ DrawReturnProgressBar();
+
+ return nRetVal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw progress bar above the flag indicating when it will return
+//-----------------------------------------------------------------------------
+void CCaptureFlagReturnIcon::DrawReturnProgressBar( void )
+{
+ CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag * > ( GetOwnerEntity() );
+
+ if ( !pFlag )
+ return;
+
+ // Don't draw if this flag is not going to reset
+ if ( pFlag->GetMaxResetTime() <= 0 )
+ return;
+
+ if ( !TFGameRules()->FlagsMayBeCapped() )
+ return;
+
+ if ( !m_pReturnProgressMaterial_Full )
+ {
+ m_pReturnProgressMaterial_Full = materials->FindMaterial( "VGUI/flagtime_full", TEXTURE_GROUP_VGUI );
+ }
+
+ if ( !m_pReturnProgressMaterial_Empty )
+ {
+ m_pReturnProgressMaterial_Empty = materials->FindMaterial( "VGUI/flagtime_empty", TEXTURE_GROUP_VGUI );
+ }
+
+ if ( !m_pReturnProgressMaterial_Full || !m_pReturnProgressMaterial_Empty )
+ {
+ return;
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ Vector vOrigin = GetAbsOrigin();
+ QAngle vAngle = vec3_angle;
+
+ // Align it towards the viewer
+ Vector vUp = CurrentViewUp();
+ Vector vRight = CurrentViewRight();
+ if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on
+ return;
+
+ vRight.z = 0;
+ VectorNormalize( vRight );
+
+ float flSize = cl_flag_return_size.GetFloat();
+
+ unsigned char ubColor[4];
+ ubColor[3] = 255;
+
+ switch( pFlag->GetTeamNumber() )
+ {
+ case TF_TEAM_RED:
+ ubColor[0] = 255;
+ ubColor[1] = 0;
+ ubColor[2] = 0;
+ break;
+ case TF_TEAM_BLUE:
+ ubColor[0] = 0;
+ ubColor[1] = 0;
+ ubColor[2] = 255;
+ break;
+ default:
+ ubColor[0] = 200;
+ ubColor[1] = 200;
+ ubColor[2] = 200;
+ break;
+ }
+
+ // First we draw a quad of a complete icon, background
+ CMeshBuilder meshBuilder;
+
+ pRenderContext->Bind( m_pReturnProgressMaterial_Empty );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ meshBuilder.Color4ubv( ubColor );
+ meshBuilder.TexCoord2f( 0,0,0 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ubv( ubColor );
+ meshBuilder.TexCoord2f( 0,1,0 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ubv( ubColor );
+ meshBuilder.TexCoord2f( 0,1,1 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ubv( ubColor );
+ meshBuilder.TexCoord2f( 0,0,1 );
+ meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+
+ pMesh->Draw();
+
+ float flProgress = pFlag->GetReturnProgress();
+
+ pRenderContext->Bind( m_pReturnProgressMaterial_Full );
+ pMesh = pRenderContext->GetDynamicMesh();
+
+ vRight *= flSize * 2;
+ vUp *= flSize * -2;
+
+ // Next we're drawing the circular progress bar, in 8 segments
+ // For each segment, we calculate the vertex position that will draw
+ // the slice.
+ int i;
+ for ( i=0;i<8;i++ )
+ {
+ if ( flProgress < Segments[i].maxProgress )
+ {
+ CMeshBuilder meshBuilder_Full;
+
+ meshBuilder_Full.Begin( pMesh, MATERIAL_TRIANGLES, 3 );
+
+ // vert 0 is ( 0.5, 0.5 )
+ meshBuilder_Full.Color4ubv( ubColor );
+ meshBuilder_Full.TexCoord2f( 0, 0.5, 0.5 );
+ meshBuilder_Full.Position3fv( vOrigin.Base() );
+ meshBuilder_Full.AdvanceVertex();
+
+ // Internal progress is the progress through this particular slice
+ float internalProgress = RemapVal( flProgress, Segments[i].maxProgress - 0.125, Segments[i].maxProgress, 0.0, 1.0 );
+ internalProgress = clamp( internalProgress, 0.0f, 1.0f );
+
+ // Calculate the x,y of the moving vertex based on internal progress
+ float swipe_x = Segments[i].vert2x - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_x;
+ float swipe_y = Segments[i].vert2y - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_y;
+
+ // vert 1 is calculated from progress
+ meshBuilder_Full.Color4ubv( ubColor );
+ meshBuilder_Full.TexCoord2f( 0, swipe_x, swipe_y );
+ meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( swipe_x - 0.5 ) ) + (vUp *( swipe_y - 0.5 ) ) ).Base() );
+ meshBuilder_Full.AdvanceVertex();
+
+ // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y )
+ meshBuilder_Full.Color4ubv( ubColor );
+ meshBuilder_Full.TexCoord2f( 0, Segments[i].vert2x, Segments[i].vert2y );
+ meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( Segments[i].vert2x - 0.5 ) ) + (vUp *( Segments[i].vert2y - 0.5 ) ) ).Base() );
+ meshBuilder_Full.AdvanceVertex();
+
+ meshBuilder_Full.End();
+
+ pMesh->Draw();
+ }
+ }
+}
+
+#endif