Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3100 lines
85 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: CTF Flag.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "entity_capture_flag.h"
  8. #include "tf_gamerules.h"
  9. #include "tf_shareddefs.h"
  10. #include "filesystem.h"
  11. #include "tf_logic_player_destruction.h"
  12. #ifdef CLIENT_DLL
  13. #include <vgui_controls/Panel.h>
  14. #include <vgui_controls/ImagePanel.h>
  15. #include <vgui_controls/EditablePanel.h>
  16. #include <vgui/IScheme.h>
  17. #include "hudelement.h"
  18. #include "iclientmode.h"
  19. #include "hud_numericdisplay.h"
  20. #include "tf_imagepanel.h"
  21. #include "c_tf_player.h"
  22. #include "c_tf_team.h"
  23. #include "tf_hud_objectivestatus.h"
  24. #include "view.h"
  25. ConVar cl_flag_return_size( "cl_flag_return_size", "20", FCVAR_CHEAT );
  26. extern ConVar tf_rd_flag_ui_mode;
  27. #else
  28. #include "tf_player.h"
  29. #include "tf_team.h"
  30. #include "tf_objective_resource.h"
  31. #include "tf_gamestats.h"
  32. #include "func_respawnroom.h"
  33. #include "datacache/imdlcache.h"
  34. #include "func_respawnflag.h"
  35. #include "func_capture_zone.h"
  36. #include "nav_mesh/tf_nav_mesh.h"
  37. #include "player_vs_environment/tf_population_manager.h"
  38. #include "tf_logic_robot_destruction.h"
  39. #include "tf_logic_halloween_2014.h"
  40. extern ConVar tf_flag_caps_per_round;
  41. extern ConVar tf_mvm_endless_bomb_reset;
  42. extern ConVar tf_rd_min_points_to_steal;
  43. ConVar cl_flag_return_height( "cl_flag_return_height", "82", FCVAR_CHEAT );
  44. ConVar tf_rd_return_min_time( "tf_rd_return_min_time", "30" );
  45. ConVar tf_rd_return_max_time( "tf_rd_return_max_time", "90" );
  46. #endif
  47. 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" );
  48. #ifdef STAGING_ONLY
  49. 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 );
  50. #endif // STAGING_ONLY
  51. 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 );
  52. enum
  53. {
  54. INVADE_NEUTRAL_TYPE_NONE = 0, // no neutral time
  55. INVADE_NEUTRAL_TYPE_DEFAULT, // current behavior....30 secs (TF_INVADE_NEUTRAL_TIME)
  56. INVADE_NEUTRAL_TYPE_HALF, // half the return time
  57. };
  58. enum
  59. {
  60. INVADE_SCORING_TEAM_SCORE = 0,
  61. INVADE_SCORING_TEAM_CAPTURE_COUNT,
  62. };
  63. #define FLAG_EFFECTS_NONE 0
  64. #define FLAG_EFFECTS_ALL 1
  65. #define FLAG_EFFECTS_PAPERONLY 2
  66. #define FLAG_EFFECTS_COLORONLY 3
  67. #ifdef CLIENT_DLL
  68. static void RecvProxy_IsDisabled( const CRecvProxyData *pData, void *pStruct, void *pOut )
  69. {
  70. CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
  71. bool bIsDisabled = ( pData->m_Value.m_Int > 0 );
  72. if ( pFlag )
  73. {
  74. pFlag->SetDisabled( bIsDisabled );
  75. }
  76. }
  77. static void RecvProxy_IsVisibleWhenDisabled( const CRecvProxyData *pData, void *pStruct, void *pOut )
  78. {
  79. CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
  80. bool bVisible = ( pData->m_Value.m_Int > 0 );
  81. if ( pFlag )
  82. {
  83. pFlag->SetVisibleWhenDisabled( bVisible );
  84. }
  85. }
  86. static void RecvProxy_FlagStatus( const CRecvProxyData *pData, void *pStruct, void *pOut )
  87. {
  88. CCaptureFlag *pFlag = (CCaptureFlag *) pStruct;
  89. if ( pFlag )
  90. {
  91. pFlag->SetFlagStatus( pData->m_Value.m_Int );
  92. IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
  93. if ( pEvent )
  94. {
  95. pEvent->SetInt( "entindex", pFlag->entindex() );
  96. gameeventmanager->FireEventClientSide( pEvent );
  97. }
  98. }
  99. }
  100. #endif
  101. //=============================================================================
  102. //
  103. // CTF Flag tables.
  104. //
  105. IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlag, DT_CaptureFlag )
  106. BEGIN_NETWORK_TABLE( CCaptureFlag, DT_CaptureFlag )
  107. #ifdef GAME_DLL
  108. SendPropBool( SENDINFO( m_bDisabled ) ),
  109. SendPropBool( SENDINFO( m_bVisibleWhenDisabled ) ),
  110. SendPropInt( SENDINFO( m_nType ), 5, SPROP_UNSIGNED ),
  111. SendPropInt( SENDINFO( m_nFlagStatus ), 3, SPROP_UNSIGNED ),
  112. SendPropTime( SENDINFO( m_flResetTime ) ),
  113. SendPropTime( SENDINFO( m_flNeutralTime ) ),
  114. SendPropTime( SENDINFO( m_flMaxResetTime ) ),
  115. SendPropEHandle( SENDINFO( m_hPrevOwner ) ),
  116. SendPropString( SENDINFO( m_szModel ) ),
  117. SendPropString( SENDINFO( m_szHudIcon ) ),
  118. SendPropString( SENDINFO( m_szPaperEffect ) ),
  119. SendPropString( SENDINFO( m_szTrailEffect ) ),
  120. SendPropInt( SENDINFO( m_nUseTrailEffect ), 3, SPROP_UNSIGNED ),
  121. SendPropInt( SENDINFO( m_nPointValue ) ),
  122. SendPropFloat( SENDINFO( m_flAutoCapTime ) ),
  123. SendPropBool( SENDINFO( m_bGlowEnabled ) ),
  124. SendPropFloat( SENDINFO( m_flTimeToSetPoisonous ) ),
  125. #else
  126. RecvPropInt( RECVINFO( m_bDisabled ), 0, RecvProxy_IsDisabled ),
  127. RecvPropInt( RECVINFO( m_bVisibleWhenDisabled ), 0, RecvProxy_IsVisibleWhenDisabled ),
  128. RecvPropInt( RECVINFO( m_nType ) ),
  129. RecvPropInt( RECVINFO( m_nFlagStatus ), 0, RecvProxy_FlagStatus ),
  130. RecvPropTime( RECVINFO( m_flResetTime ) ),
  131. RecvPropTime( RECVINFO( m_flNeutralTime ) ),
  132. RecvPropTime( RECVINFO( m_flMaxResetTime ) ),
  133. RecvPropEHandle( RECVINFO( m_hPrevOwner ) ),
  134. RecvPropString( RECVINFO( m_szModel ) ),
  135. RecvPropString( RECVINFO( m_szHudIcon ) ),
  136. RecvPropString( RECVINFO( m_szPaperEffect ) ),
  137. RecvPropString( RECVINFO( m_szTrailEffect ) ),
  138. RecvPropInt( RECVINFO( m_nUseTrailEffect ) ),
  139. RecvPropInt( RECVINFO( m_nPointValue ) ),
  140. RecvPropFloat( RECVINFO( m_flAutoCapTime ) ),
  141. RecvPropBool( RECVINFO( m_bGlowEnabled ) ),
  142. RecvPropFloat( RECVINFO( m_flTimeToSetPoisonous ) ),
  143. #endif
  144. END_NETWORK_TABLE()
  145. BEGIN_DATADESC( CCaptureFlag )
  146. // Keyfields.
  147. DEFINE_KEYFIELD( m_nType, FIELD_INTEGER, "GameType" ),
  148. DEFINE_KEYFIELD( m_nReturnTime, FIELD_INTEGER, "ReturnTime" ),
  149. DEFINE_KEYFIELD( m_nUseTrailEffect, FIELD_INTEGER, "trail_effect" ),
  150. DEFINE_KEYFIELD( m_nNeutralType, FIELD_INTEGER, "NeutralType" ),
  151. DEFINE_KEYFIELD( m_nScoringType, FIELD_INTEGER, "ScoringType" ),
  152. DEFINE_KEYFIELD( m_bReturnBetweenWaves, FIELD_BOOLEAN, "ReturnBetweenWaves" ),
  153. DEFINE_KEYFIELD( m_bVisibleWhenDisabled, FIELD_BOOLEAN, "VisibleWhenDisabled" ),
  154. DEFINE_KEYFIELD( m_bUseShotClockMode, FIELD_BOOLEAN, "ShotClockMode" ),
  155. DEFINE_KEYFIELD( m_nPointValue, FIELD_INTEGER, "PointValue" ),
  156. #ifdef GAME_DLL
  157. DEFINE_KEYFIELD( m_iszModel, FIELD_STRING, "flag_model" ),
  158. DEFINE_KEYFIELD( m_iszHudIcon, FIELD_STRING, "flag_icon" ),
  159. DEFINE_KEYFIELD( m_iszPaperEffect, FIELD_STRING, "flag_paper" ),
  160. DEFINE_KEYFIELD( m_iszTrailEffect, FIELD_STRING, "flag_trail" ),
  161. DEFINE_KEYFIELD( m_iszTags, FIELD_STRING, "tags" ),
  162. // Inputs.
  163. DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
  164. DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
  165. DEFINE_INPUTFUNC( FIELD_VOID, "RoundActivate", InputRoundActivate ),
  166. DEFINE_INPUTFUNC( FIELD_VOID, "ForceDrop", InputForceDrop ),
  167. DEFINE_INPUTFUNC( FIELD_VOID, "ForceReset", InputForceReset ),
  168. DEFINE_INPUTFUNC( FIELD_VOID, "ForceResetSilent", InputForceResetSilent ),
  169. DEFINE_INPUTFUNC( FIELD_VOID, "ForceResetAndDisableSilent", InputForceResetAndDisableSilent ),
  170. DEFINE_INPUTFUNC( FIELD_INTEGER, "SetReturnTime", InputSetReturnTime ),
  171. DEFINE_INPUTFUNC( FIELD_INTEGER, "ShowTimer", InputShowTimer ),
  172. DEFINE_INPUTFUNC( FIELD_INTEGER, "ForceGlowDisabled", InputForceGlowDisabled ),
  173. // Outputs.
  174. DEFINE_OUTPUT( m_outputOnReturn, "OnReturn" ),
  175. DEFINE_OUTPUT( m_outputOnPickUp, "OnPickUp" ),
  176. DEFINE_OUTPUT( m_outputOnPickUpTeam1, "OnPickupTeam1" ),
  177. DEFINE_OUTPUT( m_outputOnPickUpTeam2, "OnPickupTeam2" ),
  178. DEFINE_OUTPUT( m_outputOnDrop, "OnDrop" ),
  179. DEFINE_OUTPUT( m_outputOnCapture, "OnCapture" ),
  180. DEFINE_OUTPUT( m_OnCapTeam1, "OnCapTeam1" ),
  181. DEFINE_OUTPUT( m_OnCapTeam2, "OnCapTeam2" ),
  182. DEFINE_OUTPUT( m_OnTouchSameTeam, "OnTouchSameTeam" ),
  183. #endif
  184. END_DATADESC();
  185. LINK_ENTITY_TO_CLASS( item_teamflag, CCaptureFlag );
  186. IMPLEMENT_AUTO_LIST( ICaptureFlagAutoList );
  187. //=============================================================================
  188. //
  189. // CTF Flag functions.
  190. //
  191. CCaptureFlag::CCaptureFlag()
  192. {
  193. #ifdef CLIENT_DLL
  194. m_pGlowTrailEffect = NULL;
  195. m_pPaperTrailEffect = NULL;
  196. m_pGlowEffect = NULL;
  197. m_hOldOwner = NULL;
  198. m_bOldGlowEnabled = true;
  199. #else
  200. m_hReturnIcon = NULL;
  201. m_nReturnTime = 60;
  202. m_hInitialPlayer = NULL;
  203. m_hInitialParent = NULL;
  204. m_vecOffset.Init( 0, 0, 0 );
  205. m_iszModel = NULL_STRING;
  206. m_iszHudIcon = NULL_STRING;
  207. m_iszPaperEffect = NULL_STRING;
  208. m_iszTrailEffect = NULL_STRING;
  209. m_nPointValue = 0;
  210. m_flTimeToSetPoisonous = 0.f;
  211. // Team specific sound throttling for Special Delivery
  212. for ( int i = 0; i < ARRAYSIZE( m_flNextTeamSoundTime ); i++ )
  213. {
  214. m_flNextTeamSoundTime[i] = 0.f;
  215. }
  216. #endif
  217. m_nNeutralType = INVADE_NEUTRAL_TYPE_DEFAULT;
  218. m_nScoringType = INVADE_SCORING_TEAM_SCORE;
  219. m_bReturnBetweenWaves = true;
  220. m_bVisibleWhenDisabled = false;
  221. m_bUseShotClockMode = false;
  222. m_bGlowEnabled = true;
  223. UseClientSideAnimation();
  224. m_szModel.GetForModify()[ 0 ] = '\0';
  225. m_szHudIcon.GetForModify()[ 0 ] = '\0';
  226. m_szPaperEffect.GetForModify()[ 0 ] = '\0';
  227. m_szTrailEffect.GetForModify()[ 0 ] = '\0';
  228. m_nUseTrailEffect.Set( FLAG_EFFECTS_ALL );
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. //-----------------------------------------------------------------------------
  233. CCaptureFlag::~CCaptureFlag()
  234. {
  235. #ifndef GAME_DLL
  236. if ( m_pGlowEffect )
  237. {
  238. delete m_pGlowEffect;
  239. m_pGlowEffect = NULL;
  240. }
  241. #endif
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose:
  245. //-----------------------------------------------------------------------------
  246. unsigned int CCaptureFlag::GetItemID( void ) const
  247. {
  248. return TF_ITEM_CAPTURE_FLAG;
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. const char *CCaptureFlag::GetFlagModel( void )
  254. {
  255. if ( m_szModel[ 0 ] != '\0' )
  256. {
  257. if ( g_pFullFileSystem->FileExists( m_szModel.Get(), "GAME" ) )
  258. {
  259. return ( m_szModel.Get() );
  260. }
  261. }
  262. return TF_FLAG_MODEL;
  263. }
  264. void CCaptureFlag::GetHudIcon( int nTeam, char *pchName, int nBuffSize )
  265. {
  266. V_snprintf( pchName, nBuffSize, "%s_%s",
  267. ( ( m_szHudIcon[ 0 ] != '\0' ) ? ( m_szHudIcon.Get() ) : ( TF_FLAG_ICON ) ),
  268. ( ( nTeam == TF_TEAM_BLUE ) ? ( "blue" ) : ( "red" ) ) );
  269. }
  270. const char *CCaptureFlag::GetPaperEffect( void )
  271. {
  272. if ( m_szPaperEffect[ 0 ] != '\0' )
  273. {
  274. return ( m_szPaperEffect.Get() );
  275. }
  276. return TF_FLAG_EFFECT;
  277. }
  278. void CCaptureFlag::GetTrailEffect( int nTeam, char *pchName, int nBuffSize )
  279. {
  280. V_snprintf( pchName, nBuffSize, "effects/%s_%s.vmt",
  281. ( ( m_szTrailEffect[ 0 ] != '\0' ) ? ( m_szTrailEffect.Get() ) : ( TF_FLAG_TRAIL ) ),
  282. ( ( nTeam == TF_TEAM_RED ) ? ( "red" ) : ( "blu" ) ) );
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Purpose: Precache the model and sounds.
  286. //-----------------------------------------------------------------------------
  287. void CCaptureFlag::Precache( void )
  288. {
  289. PrecacheModel( GetFlagModel() );
  290. PrecacheScriptSound( TF_CTF_FLAGSPAWN );
  291. PrecacheScriptSound( TF_CTF_ENEMY_STOLEN );
  292. PrecacheScriptSound( TF_CTF_ENEMY_DROPPED );
  293. PrecacheScriptSound( TF_CTF_ENEMY_CAPTURED );
  294. PrecacheScriptSound( TF_CTF_ENEMY_RETURNED );
  295. PrecacheScriptSound( TF_CTF_TEAM_STOLEN );
  296. PrecacheScriptSound( TF_CTF_TEAM_DROPPED );
  297. PrecacheScriptSound( TF_CTF_TEAM_CAPTURED );
  298. PrecacheScriptSound( TF_CTF_TEAM_RETURNED );
  299. PrecacheScriptSound( TF_AD_CAPTURED_SOUND );
  300. PrecacheScriptSound( TF_AD_ENEMY_STOLEN );
  301. PrecacheScriptSound( TF_AD_ENEMY_DROPPED );
  302. PrecacheScriptSound( TF_AD_ENEMY_CAPTURED );
  303. PrecacheScriptSound( TF_AD_ENEMY_RETURNED );
  304. PrecacheScriptSound( TF_AD_TEAM_STOLEN );
  305. PrecacheScriptSound( TF_AD_TEAM_DROPPED );
  306. PrecacheScriptSound( TF_AD_TEAM_CAPTURED );
  307. PrecacheScriptSound( TF_AD_TEAM_RETURNED );
  308. PrecacheScriptSound( TF_MVM_AD_ENEMY_STOLEN );
  309. PrecacheScriptSound( TF_MVM_AD_ENEMY_DROPPED );
  310. PrecacheScriptSound( TF_MVM_AD_ENEMY_CAPTURED );
  311. PrecacheScriptSound( TF_MVM_AD_ENEMY_RETURNED );
  312. PrecacheScriptSound( TF_INVADE_ENEMY_STOLEN );
  313. PrecacheScriptSound( TF_INVADE_ENEMY_DROPPED );
  314. PrecacheScriptSound( TF_INVADE_ENEMY_CAPTURED );
  315. PrecacheScriptSound( TF_INVADE_TEAM_STOLEN );
  316. PrecacheScriptSound( TF_INVADE_TEAM_DROPPED );
  317. PrecacheScriptSound( TF_INVADE_TEAM_CAPTURED );
  318. PrecacheScriptSound( TF_INVADE_FLAG_RETURNED );
  319. PrecacheScriptSound( TF_RESOURCE_FLAGSPAWN );
  320. PrecacheScriptSound( TF_RESOURCE_ENEMY_STOLEN );
  321. PrecacheScriptSound( TF_RESOURCE_ENEMY_DROPPED );
  322. PrecacheScriptSound( TF_RESOURCE_ENEMY_CAPTURED );
  323. PrecacheScriptSound( TF_RESOURCE_TEAM_STOLEN );
  324. PrecacheScriptSound( TF_RESOURCE_TEAM_DROPPED );
  325. PrecacheScriptSound( TF_RESOURCE_TEAM_CAPTURED );
  326. PrecacheScriptSound( TF_RESOURCE_RETURNED );
  327. PrecacheScriptSound( TF_RESOURCE_EVENT_ENEMY_STOLEN );
  328. PrecacheScriptSound( TF_RESOURCE_EVENT_ENEMY_DROPPED );
  329. PrecacheScriptSound( TF_RESOURCE_EVENT_TEAM_STOLEN );
  330. PrecacheScriptSound( TF_RESOURCE_EVENT_TEAM_DROPPED );
  331. PrecacheScriptSound( TF_RESOURCE_EVENT_RETURNED );
  332. PrecacheScriptSound( TF_RESOURCE_EVENT_NAGS );
  333. PrecacheScriptSound( TF_RESOURCE_EVENT_RED_CAPPED );
  334. PrecacheScriptSound( TF_RESOURCE_EVENT_BLUE_CAPPED );
  335. PrecacheScriptSound( TF_RD_ENEMY_STOLEN );
  336. PrecacheScriptSound( TF_RD_ENEMY_DROPPED );
  337. PrecacheScriptSound( TF_RD_ENEMY_CAPTURED );
  338. PrecacheScriptSound( TF_RD_ENEMY_RETURNED );
  339. PrecacheScriptSound( TF_RD_TEAM_STOLEN );
  340. PrecacheScriptSound( TF_RD_TEAM_DROPPED );
  341. PrecacheScriptSound( TF_RD_TEAM_CAPTURED );
  342. PrecacheScriptSound( TF_RD_TEAM_RETURNED );
  343. PrecacheScriptSound( TF_RUNE_INTEL_CAPTURED );
  344. PrecacheParticleSystem( GetPaperEffect() );
  345. char szFileName[ MAX_PATH ];
  346. GetTrailEffect( TF_TEAM_BLUE, szFileName, sizeof( szFileName ) );
  347. PrecacheModel( szFileName );
  348. GetTrailEffect( TF_TEAM_RED, szFileName, sizeof( szFileName ) );
  349. PrecacheModel( szFileName );
  350. }
  351. #ifdef CLIENT_DLL
  352. //-----------------------------------------------------------------------------
  353. // Purpose:
  354. //-----------------------------------------------------------------------------
  355. bool CCaptureFlag::ShouldDraw()
  356. {
  357. // don't draw flag on player in PD
  358. if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  359. {
  360. if ( GetMoveParent() && GetMoveParent()->IsPlayer() )
  361. {
  362. return false;
  363. }
  364. }
  365. return BaseClass::ShouldDraw();
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. bool CCaptureFlag::IsVisibleToTargetID() const
  371. {
  372. return !IsDisabled() && GetPointValue() > 0 && const_cast<CCaptureFlag*>( this )->ShouldDraw();
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose:
  376. //-----------------------------------------------------------------------------
  377. void CCaptureFlag::OnPreDataChanged( DataUpdateType_t updateType )
  378. {
  379. m_nOldTeamNumber = GetTeamNumber();
  380. m_bOldGlowEnabled = m_bGlowEnabled;
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose:
  384. //-----------------------------------------------------------------------------
  385. void CCaptureFlag::OnDataChanged( DataUpdateType_t updateType )
  386. {
  387. bool bUpdateGlow = false;
  388. if ( m_nOldTeamNumber != GetTeamNumber() )
  389. {
  390. bUpdateGlow = true;
  391. }
  392. else if ( m_hOldOwner.Get() != GetOwnerEntity() )
  393. {
  394. bUpdateGlow = true;
  395. m_hOldOwner = GetOwnerEntity();
  396. }
  397. else if ( m_bOldGlowEnabled != m_bGlowEnabled )
  398. {
  399. bUpdateGlow = true;
  400. }
  401. if ( bUpdateGlow )
  402. {
  403. UpdateGlowEffect();
  404. }
  405. CreateSiren();
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Purpose:
  409. //-----------------------------------------------------------------------------
  410. void CCaptureFlag::CreateSiren( void )
  411. {
  412. if ( m_hSirenEffect )
  413. return;
  414. int iAttachment = GetBaseAnimating()->LookupAttachment( "siren" );
  415. if ( iAttachment != INVALID_PARTICLE_ATTACHMENT )
  416. {
  417. const char* flashlightName = "cart_flashinglight";
  418. if ( GetTeamNumber() == TF_TEAM_RED )
  419. {
  420. flashlightName = "cart_flashinglight_red";
  421. }
  422. m_hSirenEffect = ParticleProp()->Create( flashlightName, PATTACH_POINT_FOLLOW, iAttachment );
  423. }
  424. }
  425. void CCaptureFlag::DestroySiren( void )
  426. {
  427. if ( m_hSirenEffect )
  428. {
  429. ParticleProp()->StopEmission( m_hSirenEffect );
  430. m_hSirenEffect = NULL;
  431. }
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Purpose:
  435. //-----------------------------------------------------------------------------
  436. void CCaptureFlag::UpdateGlowEffect( void )
  437. {
  438. if ( !m_pGlowEffect )
  439. {
  440. m_pGlowEffect = new CGlowObject( this, Vector( 0.76f, 0.76f, 0.76f ), 1.0, true );
  441. }
  442. if ( m_pGlowEffect )
  443. {
  444. if ( ShouldHideGlowEffect() )
  445. {
  446. m_pGlowEffect->SetEntity( NULL );
  447. }
  448. else
  449. {
  450. m_pGlowEffect->SetEntity( this );
  451. float r, g, b;
  452. TeamplayRoundBasedRules()->GetTeamGlowColor( GetTeamNumber(), r, g, b );
  453. m_pGlowEffect->SetColor( Vector( r, g, b ) );
  454. }
  455. }
  456. }
  457. //-----------------------------------------------------------------------------
  458. // Purpose:
  459. //-----------------------------------------------------------------------------
  460. bool CCaptureFlag::ShouldHideGlowEffect( void )
  461. {
  462. if ( !IsGlowEnabled() )
  463. {
  464. return true;
  465. }
  466. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  467. {
  468. return false;
  469. }
  470. if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION && tf_rd_flag_ui_mode.GetInt() )
  471. return true;
  472. if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION && CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
  473. {
  474. C_TFPlayer *pOwner = ToTFPlayer( m_hPrevOwner );
  475. if ( !pOwner )
  476. return true;
  477. return CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetTeamLeader( pOwner->GetTeamNumber() ) != pOwner;
  478. }
  479. // If the opposite team stole our intel we need to hide the glow
  480. bool bIsHiddenTeam = false;
  481. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  482. if ( pLocalPlayer )
  483. {
  484. if ( m_nType == TF_FLAGTYPE_CTF )
  485. {
  486. // In CTF the flag is the team of where it was originally sitting
  487. bIsHiddenTeam = ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() );
  488. }
  489. else
  490. {
  491. // In non-CTF control the flag changes to the team that's carrying it
  492. bIsHiddenTeam = ( pLocalPlayer->GetTeamNumber() != TEAM_SPECTATOR && pLocalPlayer->GetTeamNumber() != GetTeamNumber() );
  493. }
  494. if ( pLocalPlayer->m_Shared.IsFullyInvisible() )
  495. {
  496. C_TFPlayer *pOwner = ToTFPlayer( m_hPrevOwner );
  497. if ( pOwner && pOwner != pLocalPlayer )
  498. return true;
  499. }
  500. }
  501. bool bHide = IsStolen() && bIsHiddenTeam;
  502. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  503. {
  504. bHide = IsHome();
  505. }
  506. return ( IsDisabled() || bHide || IsEffectActive( EF_NODRAW ) );
  507. }
  508. #endif //#ifdef CLIENT_DLL
  509. //-----------------------------------------------------------------------------
  510. // Purpose:
  511. //-----------------------------------------------------------------------------
  512. void CCaptureFlag::Spawn( void )
  513. {
  514. #ifdef GAME_DLL
  515. V_strncpy( m_szModel.GetForModify(), STRING( m_iszModel ), MAX_PATH );
  516. V_strncpy( m_szHudIcon.GetForModify(), STRING( m_iszHudIcon ), MAX_PATH );
  517. V_strncpy( m_szPaperEffect.GetForModify(), STRING( m_iszPaperEffect ), MAX_PATH );
  518. V_strncpy( m_szTrailEffect.GetForModify(), STRING( m_iszTrailEffect ), MAX_PATH );
  519. #endif
  520. // Precache the model and sounds. Set the flag model.
  521. Precache();
  522. SetModel( GetFlagModel() );
  523. // Set the flag solid and the size for touching.
  524. SetSolid( SOLID_BBOX );
  525. #ifdef STAGING_ONLY
  526. SetSolidFlags( FSOLID_TRIGGER );
  527. SetSize( vec3_origin, vec3_origin );
  528. SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  529. #else
  530. SetSolidFlags( FSOLID_NOT_SOLID | FSOLID_TRIGGER );
  531. SetSize( vec3_origin, vec3_origin );
  532. #endif
  533. // Bloat the box for player pickup
  534. CollisionProp()->UseTriggerBounds( true, 24 );
  535. // use the initial dynamic prop "m_bStartDisabled" setting to set our own m_bDisabled flag
  536. #ifdef GAME_DLL
  537. m_bDisabled = m_bStartDisabled;
  538. m_bStartDisabled = false;
  539. m_bInstantTrailRemove = false;
  540. m_flTimeToSetPoisonous = 0.f;
  541. // Don't allow the intelligence to fade.
  542. m_flFadeScale = 0.0f;
  543. #else
  544. m_bDisabled = false;
  545. #endif
  546. // Base class spawn.
  547. BaseClass::Spawn();
  548. // Force specific collision bounds!
  549. // This is to prevent a case where the flag can fall through the world
  550. // If the model's bounds reach outside the player's from player center
  551. SetCollisionBounds( Vector( -19.5f, -22.5f, -6.5f ), Vector( 19.5f, 22.5f, 6.5f ) );
  552. #ifdef GAME_DLL
  553. // Save the starting position, so we can reset the flag later if need be.
  554. m_vecResetPos = GetAbsOrigin();
  555. m_vecResetAng = GetAbsAngles();
  556. CBaseEntity *pParent = GetParent();
  557. if ( pParent )
  558. {
  559. m_hInitialParent = pParent;
  560. m_vecOffset = GetAbsOrigin() - pParent->GetAbsOrigin();
  561. }
  562. SetFlagStatus( TF_FLAGINFO_HOME );
  563. ResetFlagReturnTime();
  564. ResetFlagNeutralTime();
  565. m_hInitialPlayer = NULL;
  566. m_bAllowOwnerPickup = true;
  567. m_hPrevOwner = NULL;
  568. m_bCaptured = false;
  569. // update the objective resource so clients have the information
  570. if ( TFObjectiveResource() )
  571. {
  572. TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
  573. TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
  574. TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
  575. }
  576. const char* tags = STRING( m_iszTags );
  577. CSplitString splitTags( tags, " " );
  578. for ( int i=0; i<splitTags.Count(); ++i )
  579. {
  580. m_tags.CopyAndAddToTail( splitTags[i] );
  581. }
  582. #endif
  583. SetDisabled( m_bDisabled );
  584. }
  585. void CCaptureFlag::UpdateOnRemove( void )
  586. {
  587. #ifndef GAME_DLL
  588. DestroySiren();
  589. #endif
  590. // This makes the player stop glowing
  591. CTFPlayer *pOwnerPlayer = dynamic_cast< CTFPlayer * >( GetOwnerEntity() );
  592. if ( pOwnerPlayer )
  593. {
  594. pOwnerPlayer->SetItem( NULL );
  595. }
  596. BaseClass::UpdateOnRemove();
  597. }
  598. #ifdef GAME_DLL
  599. //-----------------------------------------------------------------------------
  600. // Purpose:
  601. //-----------------------------------------------------------------------------
  602. void CCaptureFlag::PlaySound( IRecipientFilter& filter, const char *pszString, int iTeam /*= TEAM_ANY */ )
  603. {
  604. // Note: iTeam parameter is only used for rate-limiting flag sounds based on team, and does not affect
  605. // who the sound is targetted at; the filter parameter is the only thing that will affect who hears this.
  606. if ( TFGameRules()->IsMannVsMachineMode() )
  607. {
  608. // Don't play bomb announcements unless we're at least 5 seconds into a wave in MVM
  609. if ( !( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING && gpGlobals->curtime - TFGameRules()->GetLastRoundStateChangeTime() >= 5.0f ) )
  610. {
  611. return;
  612. }
  613. // Only play the reset sound in MVM
  614. // Other flag announcements are too noisy
  615. if ( V_strcmp( pszString, TF_MVM_AD_ENEMY_RETURNED ) == 0 )
  616. {
  617. EmitSound( filter, entindex(), pszString );
  618. }
  619. }
  620. else if ( TFGameRules()->IsPlayingSpecialDeliveryMode() && ( ( V_strcmp( pszString, TF_RESOURCE_TEAM_DROPPED ) == 0 ) || ( V_strcmp( pszString, TF_RESOURCE_EVENT_TEAM_DROPPED ) == 0 ) ) )
  621. {
  622. // Rate limit certain flag sounds in Special Delivery
  623. if ( iTeam == TEAM_ANY || gpGlobals->curtime >= m_flNextTeamSoundTime[iTeam] )
  624. {
  625. EmitSound( filter, entindex(), pszString );
  626. if ( iTeam != TEAM_ANY )
  627. {
  628. m_flNextTeamSoundTime[iTeam] = gpGlobals->curtime + 20.0f;
  629. }
  630. }
  631. }
  632. else
  633. {
  634. EmitSound( filter, entindex(), pszString );
  635. }
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Purpose: Gets the return time for first down mode, if enabled and supported, otherwise
  639. // simply returns the passed in nReturnTime.
  640. //-----------------------------------------------------------------------------
  641. int CCaptureFlag::GetReturnTimeShotClockMode(int nStartReturnTime)
  642. {
  643. int nReturnTime = nStartReturnTime;
  644. // Only enable this for specific modes.
  645. if (IsFlagShotClockModePossible())
  646. {
  647. if (m_bUseShotClockMode)
  648. {
  649. // When the game is in a standoff (both flags are stolen and poisonous), return when next dropped
  650. // This makes it easier to resolve the standoff and continue the game
  651. if ( TFGameRules() && TFGameRules()->IsPowerupMode() && TFGameRules()->PowerupModeFlagStandoffActive() )
  652. {
  653. return 0;
  654. }
  655. float flCreditTime = (gpGlobals->curtime - m_flLastPickupTime) * tf_flag_return_time_credit_factor.GetFloat()
  656. + m_flLastResetDuration;
  657. int nPossibleCreditTime = RoundFloatToInt(flCreditTime);
  658. int nActualCreditTime = MAX(0, nPossibleCreditTime);
  659. nReturnTime = MIN(nStartReturnTime, nActualCreditTime);
  660. }
  661. }
  662. return nReturnTime;
  663. }
  664. //-----------------------------------------------------------------------------
  665. // Purpose:
  666. //-----------------------------------------------------------------------------
  667. CCaptureFlag &CCaptureFlag::operator=( const CCaptureFlag& rhs )
  668. {
  669. m_bDisabled = rhs.m_bDisabled;
  670. m_nType = rhs.m_nType;
  671. m_nReturnTime = rhs.m_nReturnTime;
  672. m_nUseTrailEffect = rhs.m_nUseTrailEffect;
  673. m_nNeutralType = rhs.m_nNeutralType;
  674. m_nScoringType = rhs.m_nScoringType;
  675. m_bReturnBetweenWaves = rhs.m_bReturnBetweenWaves;
  676. m_bVisibleWhenDisabled = rhs.m_bVisibleWhenDisabled;
  677. m_iszModel = rhs.m_iszModel;
  678. m_iszHudIcon = rhs.m_iszHudIcon;
  679. m_iszPaperEffect = rhs.m_iszPaperEffect;
  680. m_iszTrailEffect = rhs.m_iszTrailEffect;
  681. m_iszTags = rhs.m_iszTags;
  682. m_nSkin = rhs.m_nSkin;
  683. m_bUseShotClockMode = rhs.m_bUseShotClockMode;
  684. ChangeTeam( rhs.GetTeamNumber() );
  685. return *this;
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose:
  689. //-----------------------------------------------------------------------------
  690. void CCaptureFlag::Activate( void )
  691. {
  692. BaseClass::Activate();
  693. m_iOriginalTeam = GetTeamNumber();
  694. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  695. }
  696. //-----------------------------------------------------------------------------
  697. // Purpose:
  698. //-----------------------------------------------------------------------------
  699. CCaptureFlag* CCaptureFlag::Create( const Vector& vecOrigin, const char *pszModelName, ETFFlagType type )
  700. {
  701. CCaptureFlag *pFlag = static_cast< CCaptureFlag* >( CBaseEntity::CreateNoSpawn( "item_teamflag", vecOrigin, vec3_angle, NULL ) );
  702. pFlag->m_iszModel = MAKE_STRING( pszModelName );
  703. pFlag->m_nType = type;
  704. // don't show trail effect for PD flag
  705. if ( type == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  706. {
  707. pFlag->m_nUseTrailEffect = FLAG_EFFECTS_NONE;
  708. }
  709. DispatchSpawn( pFlag );
  710. return pFlag;
  711. }
  712. #endif
  713. //-----------------------------------------------------------------------------
  714. // Purpose: Reset the flag position state.
  715. //-----------------------------------------------------------------------------
  716. void CCaptureFlag::Reset( void )
  717. {
  718. #ifdef GAME_DLL
  719. m_bInstantTrailRemove = true;
  720. RemoveFlagTrail();
  721. if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION && !IsDisabled() )
  722. {
  723. if ( m_nPointValue > 0 )
  724. {
  725. // Score points!
  726. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  727. {
  728. CTFRobotDestructionLogic::GetRobotDestructionLogic()->ScorePoints( GetTeamNumber()
  729. , m_nPointValue.Get()
  730. , SCORE_REACTOR_RETURNED
  731. , NULL );
  732. CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
  733. m_nPointValue = 0;
  734. }
  735. }
  736. // Disable ourselves if our team currently has no points
  737. SetDisabled( CTFRobotDestructionLogic::GetRobotDestructionLogic()->GetTargetScore( GetTeamNumber() ) < tf_rd_min_points_to_steal.GetInt() );
  738. }
  739. else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  740. {
  741. UTIL_Remove( this );
  742. return;
  743. }
  744. // Set the flag position.
  745. if ( !m_hInitialParent.Get() )
  746. {
  747. SetAbsOrigin( m_vecResetPos );
  748. SetParent( NULL );
  749. }
  750. else
  751. {
  752. SetAbsOrigin( m_hInitialParent->GetAbsOrigin() + m_vecOffset );
  753. SetParent( m_hInitialParent.Get() );
  754. }
  755. SetAbsAngles( m_vecResetAng );
  756. // No longer dropped, if it was.
  757. SetFlagStatus( TF_FLAGINFO_HOME );
  758. ResetFlagReturnTime();
  759. ResetFlagNeutralTime();
  760. m_hInitialPlayer = NULL;
  761. m_bAllowOwnerPickup = true;
  762. m_hPrevOwner = NULL;
  763. m_flTimeToSetPoisonous = 0.f;
  764. if ( m_nType == TF_FLAGTYPE_INVADE || m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  765. {
  766. ChangeTeam( m_iOriginalTeam );
  767. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  768. }
  769. SetMoveType( MOVETYPE_NONE );
  770. // update the objective resource so clients have the information
  771. if ( TFObjectiveResource() )
  772. {
  773. TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
  774. TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
  775. TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
  776. }
  777. #endif
  778. }
  779. //-----------------------------------------------------------------------------
  780. // Purpose:
  781. //-----------------------------------------------------------------------------
  782. void CCaptureFlag::ResetMessage( void )
  783. {
  784. #ifdef GAME_DLL
  785. if ( m_nType == TF_FLAGTYPE_CTF )
  786. {
  787. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  788. {
  789. if ( iTeam == GetTeamNumber() )
  790. {
  791. CTeamRecipientFilter filter( iTeam, true );
  792. PlaySound( filter, TF_CTF_ENEMY_RETURNED );
  793. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_RETURNED );
  794. }
  795. else
  796. {
  797. CTeamRecipientFilter filter( iTeam, true );
  798. PlaySound( filter, TF_CTF_TEAM_RETURNED );
  799. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_RETURNED );
  800. }
  801. }
  802. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  803. if ( event )
  804. {
  805. event->SetInt( "eventtype", TF_FLAGEVENT_RETURNED );
  806. event->SetInt( "priority", 8 );
  807. event->SetInt( "team", GetTeamNumber() );
  808. gameeventmanager->FireEvent( event );
  809. }
  810. // Returned sound
  811. CPASAttenuationFilter filter( this, TF_CTF_FLAGSPAWN );
  812. PlaySound( filter, TF_CTF_FLAGSPAWN );
  813. }
  814. else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
  815. {
  816. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  817. {
  818. if ( iTeam == GetTeamNumber() )
  819. {
  820. CTeamRecipientFilter filter( iTeam, true );
  821. PlaySound( filter, TF_AD_TEAM_RETURNED );
  822. }
  823. else
  824. {
  825. CTeamRecipientFilter filter( iTeam, true );
  826. if ( TFGameRules()->IsMannVsMachineMode() )
  827. {
  828. PlaySound( filter, TF_MVM_AD_ENEMY_RETURNED );
  829. }
  830. else
  831. {
  832. PlaySound( filter, TF_AD_ENEMY_RETURNED );
  833. }
  834. }
  835. }
  836. }
  837. else if ( m_nType == TF_FLAGTYPE_INVADE )
  838. {
  839. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  840. {
  841. CTeamRecipientFilter filter( iTeam, true );
  842. PlaySound( filter, TF_INVADE_FLAG_RETURNED );
  843. }
  844. }
  845. else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  846. {
  847. const char *pszSound = TF_RESOURCE_RETURNED;
  848. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  849. {
  850. TFGameRules()->StartDoomsdayTicketsTimer();
  851. pszSound = TF_RESOURCE_EVENT_RETURNED;
  852. }
  853. TFGameRules()->BroadcastSound( 255, pszSound );
  854. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  855. if ( event )
  856. {
  857. event->SetInt( "eventtype", TF_FLAGEVENT_RETURNED );
  858. event->SetInt( "priority", 8 );
  859. event->SetInt( "team", GetTeamNumber() );
  860. gameeventmanager->FireEvent( event );
  861. }
  862. // Returned sound
  863. CPASAttenuationFilter filter( this, TF_RESOURCE_FLAGSPAWN );
  864. PlaySound( filter, TF_RESOURCE_FLAGSPAWN );
  865. }
  866. else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  867. {
  868. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  869. {
  870. if ( iTeam == GetTeamNumber() )
  871. {
  872. CTeamRecipientFilter filter( iTeam, true );
  873. PlaySound( filter, TF_RD_ENEMY_RETURNED );
  874. }
  875. else
  876. {
  877. CTeamRecipientFilter filter( iTeam, true );
  878. PlaySound( filter, TF_RD_TEAM_RETURNED );
  879. }
  880. }
  881. }
  882. // Output.
  883. m_outputOnReturn.FireOutput( this, this );
  884. DestroyReturnIcon();
  885. #endif
  886. }
  887. //-----------------------------------------------------------------------------
  888. // Purpose:
  889. //-----------------------------------------------------------------------------
  890. void CCaptureFlag::FlagTouch( CBaseEntity *pOther )
  891. {
  892. // Is the flag disabled or stolen already?
  893. if ( IsDisabled() || IsStolen() )
  894. {
  895. return;
  896. }
  897. // The touch from a live player.
  898. if ( !pOther->IsPlayer() || !pOther->IsAlive() )
  899. {
  900. return;
  901. }
  902. #ifdef GAME_DLL
  903. // Don't let the person who threw this flag pick it up until it hits the ground.
  904. // This way we can throw the flag to people, but not touch it as soon as we throw it ourselves
  905. if( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pOther && m_bAllowOwnerPickup == false )
  906. {
  907. return;
  908. }
  909. #endif
  910. if ( pOther->GetTeamNumber() == GetTeamNumber() )
  911. {
  912. #ifdef GAME_DLL
  913. m_OnTouchSameTeam.FireOutput( this, this );
  914. #endif
  915. // Does my team own this flag? If so, no touch.
  916. if ( m_nType == TF_FLAGTYPE_CTF || m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  917. {
  918. if ( !tf_flag_return_on_touch.GetBool() )
  919. return;
  920. if ( IsHome() || IsStolen() )
  921. return;
  922. }
  923. }
  924. if ( ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND || m_nType == TF_FLAGTYPE_TERRITORY_CONTROL ) &&
  925. pOther->GetTeamNumber() != GetTeamNumber() )
  926. {
  927. return;
  928. }
  929. if ( ( m_nType == TF_FLAGTYPE_INVADE || m_nType == TF_FLAGTYPE_RESOURCE_CONTROL ) && ( GetTeamNumber() != TEAM_UNASSIGNED ) )
  930. {
  931. if ( pOther->GetTeamNumber() != GetTeamNumber() )
  932. {
  933. return;
  934. }
  935. }
  936. // Can't pickup flags during WaitingForPlayers
  937. if ( TFGameRules()->IsInWaitingForPlayers() )
  938. return;
  939. // Get the touching player.
  940. CTFPlayer *pPlayer = ToTFPlayer( pOther );
  941. if ( !pPlayer )
  942. {
  943. return;
  944. }
  945. if ( !pPlayer->IsAllowedToPickUpFlag() )
  946. {
  947. return;
  948. }
  949. #ifdef GAME_DLL
  950. if ( TFGameRules()->IsMannVsMachineMode() )
  951. {
  952. // skip all the restrictions and let bots pick up the flag
  953. PickUp( pPlayer, true );
  954. return;
  955. }
  956. #endif
  957. // Is the touching player about to teleport?
  958. if ( pPlayer->m_Shared.InCond( TF_COND_SELECTED_TO_TELEPORT ) )
  959. return;
  960. // Don't let invulnerable players pickup flags, except in PD
  961. if ( pPlayer->m_Shared.IsInvulnerable() && m_nType != TF_FLAGTYPE_PLAYER_DESTRUCTION )
  962. return;
  963. // Don't let stealthed spies pickup the flag
  964. if ( pPlayer->m_Shared.IsStealthed() || pPlayer->m_Shared.InCond( TF_COND_STEALTHED_BLINK ) || pPlayer->m_Shared.GetPercentInvisible() > 0.25f )
  965. return;
  966. // Don't let phased scouts pickup flags
  967. if ( pPlayer->m_Shared.InCond( TF_COND_PHASE ) )
  968. return;
  969. // Don't let players carry multiple flags for user-made maps with >1
  970. if ( pPlayer->HasTheFlag() && !( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION || m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION ) && !tf_flag_return_on_touch.GetBool() )
  971. return;
  972. #ifdef GAME_DLL
  973. if ( PointInRespawnRoom(pPlayer,pPlayer->WorldSpaceCenter()) )
  974. return;
  975. #endif
  976. if ( IsDropped() && ( pOther->GetTeamNumber() == GetTeamNumber() ) && ( m_nType == TF_FLAGTYPE_CTF ) && tf_flag_return_on_touch.GetBool() )
  977. {
  978. Reset();
  979. ResetMessage();
  980. #ifdef GAME_DLL
  981. CTF_GameStats.Event_PlayerReturnedFlag( pPlayer );
  982. #endif
  983. }
  984. else
  985. {
  986. // Pick up the flag.
  987. PickUp( pPlayer, true );
  988. }
  989. }
  990. //-----------------------------------------------------------------------------
  991. // Purpose:
  992. //-----------------------------------------------------------------------------
  993. void CCaptureFlag::PickUp( CTFPlayer *pPlayer, bool bInvisible )
  994. {
  995. // Is the flag enabled?
  996. if ( IsDisabled() )
  997. return;
  998. if ( !TFGameRules()->FlagsMayBeCapped() )
  999. return;
  1000. // For maps/scenarios with multiple flags, only allow one flag per player
  1001. if ( TFGameRules()->IsMannVsMachineMode() )
  1002. {
  1003. if ( pPlayer->HasTheFlag() )
  1004. return;
  1005. }
  1006. #ifdef GAME_DLL
  1007. if ( !m_bAllowOwnerPickup )
  1008. {
  1009. if ( m_hPrevOwner.Get() && m_hPrevOwner.Get() == pPlayer )
  1010. {
  1011. return;
  1012. }
  1013. }
  1014. if ( TFGameRules()->IsMannVsMachineMode() && pPlayer->IsBot() )
  1015. {
  1016. CTFBot *pBot = assert_cast< CTFBot* >( pPlayer );
  1017. if ( pBot->HasAttribute( CTFBot::IGNORE_FLAG ) )
  1018. return;
  1019. pBot->SetFlagTarget( this );
  1020. }
  1021. if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  1022. {
  1023. if ( pPlayer->HasItem() && ( pPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG ) && pPlayer->GetItem() != this )
  1024. {
  1025. CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
  1026. // If the player who touched us is on the other team and is already carrying a flag, add our score
  1027. // onto the flag that they're carrying
  1028. CCaptureFlag* pOtherFlag = static_cast< CCaptureFlag * >( pPlayer->GetItem() );
  1029. pOtherFlag->AddPointValue( m_nPointValue );
  1030. UTIL_Remove( this );
  1031. return;
  1032. }
  1033. }
  1034. else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  1035. {
  1036. if ( pPlayer->HasItem() && ( pPlayer->GetItem()->GetItemID() == TF_ITEM_CAPTURE_FLAG ) && pPlayer->GetItem() != this )
  1037. {
  1038. // If the player who touched us is already carrying a flag, add our score
  1039. // onto the flag that they're carrying
  1040. CCaptureFlag* pOtherFlag = static_cast< CCaptureFlag * >( pPlayer->GetItem() );
  1041. pOtherFlag->AddPointValue( m_nPointValue );
  1042. UTIL_Remove( this );
  1043. if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
  1044. {
  1045. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
  1046. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropPickupSound( pPlayer );
  1047. }
  1048. return;
  1049. }
  1050. }
  1051. #endif
  1052. // Call into the base class pickup.
  1053. BaseClass::PickUp( pPlayer, false );
  1054. pPlayer->TeamFortress_SetSpeed();
  1055. #ifdef GAME_DLL
  1056. // Update the parent to set the correct place on the model to attach the flag.
  1057. int iAttachment = pPlayer->LookupAttachment( "flag" );
  1058. if( iAttachment > 0 )
  1059. {
  1060. SetParent( pPlayer, iAttachment );
  1061. SetLocalOrigin( vec3_origin );
  1062. SetLocalAngles( vec3_angle );
  1063. }
  1064. // Remove the player's disguise if they're a spy, but not in PD
  1065. if ( pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SPY && m_nType != TF_FLAGTYPE_PLAYER_DESTRUCTION )
  1066. {
  1067. if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) ||
  1068. pPlayer->m_Shared.InCond( TF_COND_DISGUISING ))
  1069. {
  1070. pPlayer->m_Shared.RemoveDisguise();
  1071. }
  1072. }
  1073. // switch to brighter picked up skin
  1074. m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
  1075. // Remove the touch function.
  1076. SetTouch( NULL );
  1077. m_hPrevOwner = pPlayer;
  1078. m_bAllowOwnerPickup = true;
  1079. if ( m_hInitialPlayer.Get() == NULL )
  1080. {
  1081. m_hInitialPlayer = pPlayer;
  1082. }
  1083. if ( m_nType == TF_FLAGTYPE_CTF )
  1084. {
  1085. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1086. {
  1087. if ( iTeam != pPlayer->GetTeamNumber() )
  1088. {
  1089. CTeamRecipientFilter filter( iTeam, true );
  1090. PlaySound( filter, TF_CTF_ENEMY_STOLEN );
  1091. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_TAKEN );
  1092. }
  1093. else
  1094. {
  1095. CTeamRecipientFilter filter( iTeam, true );
  1096. PlaySound( filter, TF_CTF_TEAM_STOLEN );
  1097. // exclude the guy who just picked it up
  1098. filter.RemoveRecipient( pPlayer );
  1099. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_TAKEN );
  1100. }
  1101. }
  1102. }
  1103. else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
  1104. {
  1105. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1106. {
  1107. if ( iTeam != pPlayer->GetTeamNumber() )
  1108. {
  1109. CTeamRecipientFilter filter( iTeam, true );
  1110. if ( TFGameRules()->IsMannVsMachineMode() )
  1111. {
  1112. PlaySound( filter, TF_MVM_AD_ENEMY_STOLEN );
  1113. }
  1114. else
  1115. {
  1116. PlaySound( filter, TF_AD_ENEMY_STOLEN );
  1117. }
  1118. }
  1119. else
  1120. {
  1121. CTeamRecipientFilter filter( iTeam, true );
  1122. PlaySound( filter, TF_AD_TEAM_STOLEN );
  1123. }
  1124. }
  1125. }
  1126. else if ( m_nType == TF_FLAGTYPE_INVADE )
  1127. {
  1128. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1129. {
  1130. if ( iTeam != pPlayer->GetTeamNumber() )
  1131. {
  1132. CTeamRecipientFilter filter( iTeam, true );
  1133. PlaySound( filter, TF_INVADE_ENEMY_STOLEN );
  1134. }
  1135. else
  1136. {
  1137. CTeamRecipientFilter filter( iTeam, true );
  1138. PlaySound( filter, TF_INVADE_TEAM_STOLEN );
  1139. }
  1140. }
  1141. // set the flag's team to match the player's team
  1142. ChangeTeam( pPlayer->GetTeamNumber() );
  1143. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  1144. m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
  1145. }
  1146. else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  1147. {
  1148. // In Special delivery we only tell them about the very first flag pick up from neutral
  1149. if ( GetTeamNumber() == TEAM_UNASSIGNED )
  1150. {
  1151. bool bEventMap = TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY );
  1152. if ( bEventMap )
  1153. {
  1154. TFGameRules()->StopDoomsdayTicketsTimer();
  1155. }
  1156. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1157. {
  1158. const char *pszSound = TF_RESOURCE_ENEMY_STOLEN;
  1159. if ( iTeam != pPlayer->GetTeamNumber() )
  1160. {
  1161. if ( bEventMap )
  1162. {
  1163. pszSound = TF_RESOURCE_EVENT_ENEMY_STOLEN;
  1164. }
  1165. }
  1166. else
  1167. {
  1168. pszSound = TF_RESOURCE_TEAM_STOLEN;
  1169. if ( bEventMap )
  1170. {
  1171. pszSound = TF_RESOURCE_EVENT_TEAM_STOLEN;
  1172. }
  1173. }
  1174. CTeamRecipientFilter filter( iTeam, true );
  1175. PlaySound( filter, pszSound, iTeam );
  1176. }
  1177. }
  1178. // set the flag's team to match the player's team
  1179. ChangeTeam( pPlayer->GetTeamNumber() );
  1180. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  1181. m_nSkin = m_nSkin + TF_FLAG_NUMBEROFSKINS;
  1182. }
  1183. else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  1184. {
  1185. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1186. {
  1187. if ( iTeam != pPlayer->GetTeamNumber() )
  1188. {
  1189. CTeamRecipientFilter filter( iTeam, true );
  1190. PlaySound( filter, TF_RD_ENEMY_STOLEN, iTeam );
  1191. }
  1192. else
  1193. {
  1194. CTeamRecipientFilter filter( iTeam, true );
  1195. PlaySound( filter, TF_RD_TEAM_STOLEN, iTeam );
  1196. }
  1197. }
  1198. }
  1199. else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  1200. {
  1201. if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
  1202. {
  1203. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
  1204. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropPickupSound( pPlayer );
  1205. }
  1206. }
  1207. if ( TFGameRules() && TFGameRules()->IsPowerupMode() && m_flTimeToSetPoisonous == 0.f )
  1208. {
  1209. // replace 90.f with a convar?
  1210. m_flTimeToSetPoisonous = gpGlobals->curtime + 90.f;
  1211. }
  1212. // If the flag was at home, set the initial reset time to the max allowable time, otherwise it's to whatever it was
  1213. // right now so we can persist that until later.
  1214. if ( m_nFlagStatus == TF_FLAGINFO_HOME )
  1215. {
  1216. m_flLastResetDuration = GetMaxReturnTime();
  1217. }
  1218. else
  1219. {
  1220. m_flLastResetDuration = m_flResetTime - gpGlobals->curtime;
  1221. }
  1222. // Remember that this is when the item was picked up.
  1223. m_flLastPickupTime = gpGlobals->curtime;
  1224. int nOldFlagStatus = m_nFlagStatus;
  1225. SetFlagStatus( TF_FLAGINFO_STOLEN, pPlayer );
  1226. ResetFlagReturnTime();
  1227. ResetFlagNeutralTime();
  1228. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  1229. if ( event )
  1230. {
  1231. event->SetInt( "player", pPlayer->entindex() );
  1232. event->SetInt( "eventtype", TF_FLAGEVENT_PICKUP );
  1233. event->SetInt( "priority", 8 );
  1234. event->SetInt( "home", ( nOldFlagStatus == TF_FLAGINFO_HOME ) ? 1 : 0 );
  1235. event->SetInt( "team", GetTeamNumber() );
  1236. gameeventmanager->FireEvent( event );
  1237. }
  1238. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGPICKUP );
  1239. // Output.
  1240. m_outputOnPickUp.FireOutput( this, this );
  1241. switch ( pPlayer->GetTeamNumber() )
  1242. {
  1243. case TF_TEAM_RED:
  1244. m_outputOnPickUpTeam1.FireOutput( this, this );
  1245. break;
  1246. case TF_TEAM_BLUE:
  1247. m_outputOnPickUpTeam2.FireOutput( this, this );
  1248. break;
  1249. default:
  1250. break;
  1251. }
  1252. DestroyReturnIcon();
  1253. StartFlagTrail();
  1254. HandleFlagPickedUpInDetectionZone( pPlayer );
  1255. // update the objective resource so clients have the information
  1256. if ( TFObjectiveResource() )
  1257. {
  1258. TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
  1259. TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
  1260. TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
  1261. }
  1262. if ( TFGameRules()->IsMannVsMachineMode() )
  1263. {
  1264. if ( nOldFlagStatus == TF_FLAGINFO_HOME )
  1265. {
  1266. if ( pPlayer->IsMiniBoss() )
  1267. {
  1268. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_HAS_BOMB, TF_TEAM_PVE_DEFENDERS );
  1269. }
  1270. else
  1271. {
  1272. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_FIRST_BOMB_PICKUP, TF_TEAM_PVE_DEFENDERS );
  1273. }
  1274. }
  1275. else
  1276. {
  1277. if ( pPlayer->IsMiniBoss() )
  1278. {
  1279. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_GIANT_HAS_BOMB, TF_TEAM_PVE_DEFENDERS );
  1280. }
  1281. else
  1282. {
  1283. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_PICKUP, TF_TEAM_PVE_DEFENDERS );
  1284. }
  1285. }
  1286. }
  1287. #endif
  1288. }
  1289. //-----------------------------------------------------------------------------
  1290. // Purpose:
  1291. //-----------------------------------------------------------------------------
  1292. void CCaptureFlag::Capture( CTFPlayer *pPlayer, int nCapturePoint )
  1293. {
  1294. // Is the flag enabled?
  1295. if ( IsDisabled() )
  1296. return;
  1297. #ifdef GAME_DLL
  1298. if ( m_nType == TF_FLAGTYPE_CTF )
  1299. {
  1300. bool bNotify = true;
  1301. // don't play any sounds if this is going to win the round for one of the teams (victory sound will be played instead)
  1302. if ( tf_flag_caps_per_round.GetInt() > 0 )
  1303. {
  1304. int nCaps = TFTeamMgr()->GetFlagCaptures( pPlayer->GetTeamNumber() );
  1305. if ( ( nCaps >= 0 ) && ( tf_flag_caps_per_round.GetInt() - nCaps <= 1 ) )
  1306. {
  1307. // this cap is going to win, so don't play a sound
  1308. bNotify = false;
  1309. }
  1310. }
  1311. if ( bNotify )
  1312. {
  1313. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1314. {
  1315. if ( iTeam != pPlayer->GetTeamNumber() )
  1316. {
  1317. CTeamRecipientFilter filter( iTeam, true );
  1318. PlaySound( filter, TF_CTF_ENEMY_CAPTURED );
  1319. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_CAPTURED );
  1320. }
  1321. else
  1322. {
  1323. CTeamRecipientFilter filter( iTeam, true );
  1324. if ( TFGameRules() && TFGameRules()->IsPowerupMode() )
  1325. {
  1326. PlaySound( filter, TF_RUNE_INTEL_CAPTURED );
  1327. }
  1328. else
  1329. {
  1330. PlaySound( filter, TF_CTF_TEAM_CAPTURED );
  1331. }
  1332. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_CAPTURED );
  1333. }
  1334. }
  1335. if ( TFGameRules() )
  1336. {
  1337. TFGameRules()->HandleCTFCaptureBonus( pPlayer->GetTeamNumber() );
  1338. }
  1339. }
  1340. // Reward the player
  1341. CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
  1342. int nAmount = TFGameRules()->CalculateCurrencyAmount_ByType( TF_CURRENCY_CAPTURED_OBJECTIVE );
  1343. #ifdef STAGING_ONLY
  1344. if ( TFGameRules()->GameModeUsesExperience() )
  1345. {
  1346. pPlayer->AddExperiencePoints( nAmount );
  1347. }
  1348. #endif // STAGING_ONLY
  1349. TFGameRules()->DistributeCurrencyAmount( nAmount, pPlayer );
  1350. // if someone else stole the flag, give them credit, too
  1351. if ( m_hInitialPlayer.Get() && m_hInitialPlayer.Get() != pPlayer )
  1352. {
  1353. CTF_GameStats.Event_PlayerCapturedPoint( ToTFPlayer( m_hInitialPlayer.Get() ) );
  1354. m_hInitialPlayer = NULL;
  1355. }
  1356. // Reward the team
  1357. if ( tf_flag_caps_per_round.GetInt() > 0 )
  1358. {
  1359. TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
  1360. }
  1361. else
  1362. {
  1363. TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_CTF_CAPTURED_TEAM_SCORE );
  1364. }
  1365. }
  1366. else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
  1367. {
  1368. char szNumber[64];
  1369. Q_snprintf( szNumber, sizeof(szNumber), "%d", nCapturePoint );
  1370. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1371. {
  1372. if ( iTeam != pPlayer->GetTeamNumber() )
  1373. {
  1374. CTeamRecipientFilter filter( iTeam, true );
  1375. if ( TFGameRules()->IsMannVsMachineMode() )
  1376. {
  1377. PlaySound( filter, TF_MVM_AD_ENEMY_CAPTURED );
  1378. }
  1379. else
  1380. {
  1381. PlaySound( filter, TF_AD_ENEMY_CAPTURED );
  1382. }
  1383. }
  1384. else
  1385. {
  1386. CTeamRecipientFilter filter( iTeam, true );
  1387. PlaySound( filter, TF_AD_TEAM_CAPTURED );
  1388. }
  1389. }
  1390. // Capture sound
  1391. CBroadcastRecipientFilter filter;
  1392. PlaySound( filter, TF_AD_CAPTURED_SOUND );
  1393. // Reward the player
  1394. CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
  1395. // TFTODO:: Reward the team
  1396. }
  1397. else if ( m_nType == TF_FLAGTYPE_INVADE )
  1398. {
  1399. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1400. {
  1401. if ( iTeam != pPlayer->GetTeamNumber() )
  1402. {
  1403. CTeamRecipientFilter filter( iTeam, true );
  1404. PlaySound( filter, TF_INVADE_ENEMY_CAPTURED );
  1405. }
  1406. else
  1407. {
  1408. CTeamRecipientFilter filter( iTeam, true );
  1409. PlaySound( filter, TF_INVADE_TEAM_CAPTURED );
  1410. }
  1411. }
  1412. // Reward the player
  1413. CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
  1414. // Reward the team
  1415. if ( m_nScoringType == INVADE_SCORING_TEAM_CAPTURE_COUNT )
  1416. {
  1417. if ( tf_flag_caps_per_round.GetInt() > 0 )
  1418. {
  1419. TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
  1420. }
  1421. else
  1422. {
  1423. TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_INVADE_CAPTURED_TEAM_SCORE );
  1424. }
  1425. }
  1426. else
  1427. {
  1428. TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_INVADE_CAPTURED_TEAM_SCORE );
  1429. }
  1430. }
  1431. else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  1432. {
  1433. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  1434. {
  1435. TFGameRules()->BroadcastSound( 255, ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) ? TF_RESOURCE_EVENT_RED_CAPPED : TF_RESOURCE_EVENT_BLUE_CAPPED );
  1436. }
  1437. else
  1438. {
  1439. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1440. {
  1441. const char *pszSound = TF_RESOURCE_ENEMY_CAPTURED;
  1442. if ( iTeam == pPlayer->GetTeamNumber() )
  1443. {
  1444. pszSound = TF_RESOURCE_TEAM_CAPTURED;
  1445. }
  1446. CTeamRecipientFilter filter( iTeam, true );
  1447. PlaySound( filter, pszSound, iTeam );
  1448. }
  1449. }
  1450. // Reward the player
  1451. CTF_GameStats.Event_PlayerCapturedPoint( pPlayer );
  1452. // if someone else stole the flag, give them credit, too
  1453. if ( m_hInitialPlayer.Get() && m_hInitialPlayer.Get() != pPlayer )
  1454. {
  1455. CTF_GameStats.Event_PlayerCapturedPoint( ToTFPlayer( m_hInitialPlayer.Get() ) );
  1456. m_hInitialPlayer = NULL;
  1457. }
  1458. // Reward the team
  1459. if ( tf_flag_caps_per_round.GetInt() > 0 )
  1460. {
  1461. TFTeamMgr()->IncrementFlagCaptures( pPlayer->GetTeamNumber() );
  1462. }
  1463. else
  1464. {
  1465. TFTeamMgr()->AddTeamScore( pPlayer->GetTeamNumber(), TF_RESOURCE_CAPTURED_TEAM_SCORE );
  1466. }
  1467. }
  1468. else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  1469. {
  1470. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1471. {
  1472. if ( iTeam != pPlayer->GetTeamNumber() )
  1473. {
  1474. CTeamRecipientFilter filter( iTeam, true );
  1475. PlaySound( filter, TF_RD_ENEMY_CAPTURED, iTeam );
  1476. }
  1477. else
  1478. {
  1479. CTeamRecipientFilter filter( iTeam, true );
  1480. PlaySound( filter, TF_RD_TEAM_CAPTURED, iTeam );
  1481. }
  1482. }
  1483. // Score points!
  1484. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  1485. {
  1486. CTFRobotDestructionLogic::GetRobotDestructionLogic()->ScorePoints( pPlayer->GetTeamNumber()
  1487. , m_nPointValue.Get()
  1488. , SCORE_REACTOR_CAPTURED
  1489. , pPlayer );
  1490. CTFRobotDestructionLogic::GetRobotDestructionLogic()->FlagDestroyed( GetTeamNumber() );
  1491. m_nPointValue = 0;
  1492. }
  1493. }
  1494. if ( IsPoisonous() )
  1495. {
  1496. pPlayer->m_Shared.RemoveCond( TF_COND_MARKEDFORDEATH );
  1497. }
  1498. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  1499. if ( event )
  1500. {
  1501. event->SetInt( "player", pPlayer->entindex() );
  1502. event->SetInt( "eventtype", TF_FLAGEVENT_CAPTURE );
  1503. event->SetInt( "priority", 9 );
  1504. event->SetInt( "team", GetTeamNumber() );
  1505. gameeventmanager->FireEvent( event );
  1506. }
  1507. SetFlagStatus( TF_FLAGINFO_HOME );
  1508. ResetFlagReturnTime();
  1509. ResetFlagNeutralTime();
  1510. m_bInstantTrailRemove = true;
  1511. RemoveFlagTrail();
  1512. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  1513. HandleFlagCapturedInDetectionZone( pPlayer );
  1514. HandleFlagDroppedInDetectionZone( pPlayer );
  1515. // Reset the flag.
  1516. BaseClass::Drop( pPlayer, true );
  1517. Reset();
  1518. pPlayer->TeamFortress_SetSpeed();
  1519. if ( !TFGameRules() || !TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  1520. {
  1521. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_FLAGCAPTURED );
  1522. }
  1523. // Outputs
  1524. m_outputOnCapture.FireOutput( this, this );
  1525. switch ( pPlayer->GetTeamNumber() )
  1526. {
  1527. case TF_TEAM_RED:
  1528. m_OnCapTeam1.FireOutput( this, this );
  1529. break;
  1530. case TF_TEAM_BLUE:
  1531. m_OnCapTeam2.FireOutput( this, this );
  1532. break;
  1533. default:
  1534. break;
  1535. }
  1536. m_bCaptured = true;
  1537. SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
  1538. if ( TFGameRules()->InStalemate() )
  1539. {
  1540. // whoever capped the flag is the winner, give them enough caps to win
  1541. CTFTeam *pTeam = pPlayer->GetTFTeam();
  1542. if ( !pTeam )
  1543. return;
  1544. // if we still need more caps to trigger a win, give them to us
  1545. if ( pTeam->GetFlagCaptures() < tf_flag_caps_per_round.GetInt() )
  1546. {
  1547. pTeam->SetFlagCaptures( tf_flag_caps_per_round.GetInt() );
  1548. }
  1549. }
  1550. #endif
  1551. }
  1552. //-----------------------------------------------------------------------------
  1553. // Purpose: A player drops the flag.
  1554. //-----------------------------------------------------------------------------
  1555. void CCaptureFlag::Drop( CTFPlayer *pPlayer, bool bVisible, bool bThrown /*= false*/, bool bMessage /*= true*/ )
  1556. {
  1557. // Is the flag enabled?
  1558. if ( IsDisabled() )
  1559. return;
  1560. // Call into the base class drop.
  1561. BaseClass::Drop( pPlayer, bVisible );
  1562. pPlayer->TeamFortress_SetSpeed();
  1563. #ifdef GAME_DLL
  1564. if ( bThrown )
  1565. {
  1566. m_bAllowOwnerPickup = false;
  1567. m_flOwnerPickupTime = gpGlobals->curtime + TF_FLAG_OWNER_PICKUP_TIME;
  1568. }
  1569. // Drop from the player's center so we can guarantee that it is in a valid spot
  1570. Vector vecStart = pPlayer->WorldSpaceCenter();
  1571. Vector vecEnd = vecStart;
  1572. vecEnd.z -= 8000.0f;
  1573. trace_t trace;
  1574. UTIL_TraceHull( vecStart, vecEnd, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &trace );
  1575. if ( trace.startsolid )
  1576. {
  1577. DevWarning( "Dropped flag trace started solid!\nWiggle around each axis to find a safer fit!\n" );
  1578. const float fMultipliers[ 3 ] = { 0.0f, 1.0f, -1.0f };
  1579. // Wiggle it around on each axis to find a safe place
  1580. for ( int z = 0; z < ARRAYSIZE( fMultipliers ) && trace.startsolid; z++ )
  1581. {
  1582. for ( int y = 0; y < ARRAYSIZE( fMultipliers ) && trace.startsolid; y++ )
  1583. {
  1584. for ( int x = 0; x < ARRAYSIZE( fMultipliers ) && trace.startsolid; x++ )
  1585. {
  1586. vecStart = pPlayer->WorldSpaceCenter();
  1587. vecStart += Vector( fMultipliers[ x ] * 10.0f, fMultipliers[ y ] * 10.0f, fMultipliers[ z ] * 10.0f );
  1588. vecEnd = vecStart;
  1589. vecEnd.z -= 8000.0f;
  1590. UTIL_TraceHull( vecStart, vecEnd, WorldAlignMins(), WorldAlignMaxs(), MASK_SOLID, this, COLLISION_GROUP_DEBRIS, &trace );
  1591. }
  1592. }
  1593. }
  1594. }
  1595. if ( trace.startsolid )
  1596. {
  1597. // Couldn't find a good spot... just leave it in the center of where the player died
  1598. AssertMsg( 0, "Couldn't find a safe place to drop the flag!\n" );
  1599. DevWarning( "Couldn't find a safe place to drop the flag!\nDropping at the player's center!\n" );
  1600. SetAbsOrigin( pPlayer->WorldSpaceCenter() );
  1601. }
  1602. else
  1603. {
  1604. // Found a good spot for it
  1605. SetAbsOrigin( trace.endpos );
  1606. // If it lands on an elevator, parent it to the elevator
  1607. if ( trace.m_pEnt && trace.m_pEnt->GetMoveType() == MOVETYPE_PUSH )
  1608. {
  1609. SetParent( trace.m_pEnt );
  1610. }
  1611. }
  1612. // ensure the bomb drops somewhere the bots can reach it in MvM mode
  1613. if ( TFGameRules()->IsMannVsMachineMode() )
  1614. {
  1615. Vector bombPos = GetAbsOrigin();
  1616. CTFNavArea *bombArea = (CTFNavArea *)TheNavMesh->GetNavArea( bombPos, 99999.9f );
  1617. bool isBombInBadPlace = false;
  1618. if ( bombArea )
  1619. {
  1620. if ( bombArea->HasAttributeTF( TF_NAV_BOMB_CAN_DROP_HERE ) )
  1621. {
  1622. float height = bombArea->GetZ( bombPos );
  1623. if ( height > HalfHumanHeight )
  1624. {
  1625. isBombInBadPlace = true;
  1626. }
  1627. }
  1628. else
  1629. {
  1630. // Bomb not allowed in this nav area
  1631. isBombInBadPlace = true;
  1632. }
  1633. }
  1634. else
  1635. {
  1636. // Bomb is off the mesh
  1637. isBombInBadPlace = true;
  1638. }
  1639. if ( isBombInBadPlace )
  1640. {
  1641. // the bomb has dropped in an invalid spot - move it to a nearby valid area
  1642. const float searchRange = 500.0f;
  1643. Extent nearExtent;
  1644. nearExtent.lo = bombPos;
  1645. nearExtent.lo.x -= searchRange;
  1646. nearExtent.lo.y -= searchRange;
  1647. 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
  1648. nearExtent.hi = bombPos;
  1649. nearExtent.hi.x += searchRange;
  1650. nearExtent.hi.y += searchRange;
  1651. nearExtent.hi.z += searchRange;
  1652. CUtlVector< CTFNavArea * > nearAreaVector;
  1653. TheNavMesh->CollectAreasOverlappingExtent< CTFNavArea >( nearExtent, &nearAreaVector );
  1654. CTFNavArea *nearValidArea = NULL;
  1655. float nearRangeSq = FLT_MAX;
  1656. Vector nearSpot;
  1657. for( int i=0; i<nearAreaVector.Count(); ++i )
  1658. {
  1659. CTFNavArea *area = nearAreaVector[i];
  1660. if ( area->HasAttributeTF( TF_NAV_BOMB_CAN_DROP_HERE ) )
  1661. {
  1662. area->GetClosestPointOnArea( bombPos, &nearSpot );
  1663. float rangeSq = ( nearSpot - bombPos ).LengthSqr();
  1664. if ( rangeSq < nearRangeSq )
  1665. {
  1666. nearRangeSq = rangeSq;
  1667. nearValidArea = area;
  1668. }
  1669. }
  1670. }
  1671. if ( nearValidArea )
  1672. {
  1673. nearValidArea->GetClosestPointOnArea( bombPos, &bombPos );
  1674. bombPos.z += 5.0f;
  1675. SetAbsOrigin( bombPos );
  1676. }
  1677. }
  1678. }
  1679. if ( m_nType == TF_FLAGTYPE_CTF )
  1680. {
  1681. if ( bMessage )
  1682. {
  1683. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1684. {
  1685. if ( iTeam != pPlayer->GetTeamNumber() )
  1686. {
  1687. CTeamRecipientFilter filter( iTeam, true );
  1688. PlaySound( filter, TF_CTF_ENEMY_DROPPED );
  1689. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_YOUR_FLAG_DROPPED );
  1690. }
  1691. else
  1692. {
  1693. CTeamRecipientFilter filter( iTeam, true );
  1694. PlaySound( filter, TF_CTF_TEAM_DROPPED );
  1695. TFGameRules()->SendHudNotification( filter, HUD_NOTIFY_ENEMY_FLAG_DROPPED );
  1696. }
  1697. }
  1698. }
  1699. }
  1700. else if ( m_nType == TF_FLAGTYPE_INVADE )
  1701. {
  1702. if ( bMessage )
  1703. {
  1704. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1705. {
  1706. if ( iTeam != pPlayer->GetTeamNumber() )
  1707. {
  1708. CTeamRecipientFilter filter( iTeam, true );
  1709. PlaySound( filter, TF_INVADE_ENEMY_DROPPED );
  1710. }
  1711. else
  1712. {
  1713. CTeamRecipientFilter filter( iTeam, true );
  1714. PlaySound( filter, TF_INVADE_TEAM_DROPPED );
  1715. }
  1716. }
  1717. }
  1718. if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_HALF )
  1719. {
  1720. SetFlagNeutralIn( (float)GetMaxReturnTime() / 2.0 );
  1721. }
  1722. else if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_DEFAULT )
  1723. {
  1724. // if our return time is less than the neutral time, we don't need a neutral time
  1725. if ( TF_INVADE_NEUTRAL_TIME < GetMaxReturnTime() )
  1726. {
  1727. SetFlagNeutralIn( TF_INVADE_NEUTRAL_TIME );
  1728. }
  1729. }
  1730. }
  1731. else if ( m_nType == TF_FLAGTYPE_ATTACK_DEFEND )
  1732. {
  1733. if ( bMessage )
  1734. {
  1735. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1736. {
  1737. if ( iTeam != pPlayer->GetTeamNumber() )
  1738. {
  1739. CTeamRecipientFilter filter( iTeam, true );
  1740. if ( TFGameRules()->IsMannVsMachineMode() )
  1741. {
  1742. PlaySound( filter, TF_MVM_AD_ENEMY_DROPPED );
  1743. }
  1744. else
  1745. {
  1746. PlaySound( filter, TF_AD_ENEMY_DROPPED );
  1747. }
  1748. }
  1749. else
  1750. {
  1751. CTeamRecipientFilter filter( iTeam, true );
  1752. PlaySound( filter, TF_AD_TEAM_DROPPED );
  1753. }
  1754. }
  1755. }
  1756. }
  1757. else if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  1758. {
  1759. if ( bMessage )
  1760. {
  1761. const char *pszSound = TF_RESOURCE_TEAM_DROPPED;
  1762. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  1763. {
  1764. pszSound = TF_RESOURCE_EVENT_TEAM_DROPPED;
  1765. }
  1766. // We only care about our own team dropping it in Special Delivery
  1767. int iTeam = pPlayer->GetTeamNumber();
  1768. CTeamRecipientFilter filter( iTeam, true );
  1769. PlaySound( filter, pszSound, iTeam );
  1770. }
  1771. }
  1772. else if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  1773. {
  1774. // If a player dropped this flag but the flag has less than the min to steal
  1775. // we just return the flag rather than have it exist on the ground
  1776. if ( GetPointValue() < tf_rd_min_points_to_steal.GetInt() )
  1777. {
  1778. ResetFlag();
  1779. return;
  1780. }
  1781. else if ( bMessage )
  1782. {
  1783. for ( int iTeam = TF_TEAM_RED; iTeam < TF_TEAM_COUNT; ++iTeam )
  1784. {
  1785. // We only care about our own team dropping it in Special Delivery
  1786. if ( iTeam == pPlayer->GetTeamNumber() )
  1787. {
  1788. CTeamRecipientFilter filter( iTeam, true );
  1789. PlaySound( filter, TF_RD_TEAM_DROPPED, iTeam );
  1790. }
  1791. else
  1792. {
  1793. CTeamRecipientFilter filter( iTeam, true );
  1794. PlaySound( filter, TF_RD_ENEMY_DROPPED, iTeam );
  1795. }
  1796. }
  1797. }
  1798. }
  1799. else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  1800. {
  1801. if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
  1802. {
  1803. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->CalcTeamLeader( pPlayer->GetTeamNumber() );
  1804. CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->PlayPropDropSound( pPlayer );
  1805. }
  1806. }
  1807. if ( IsPoisonous() )
  1808. {
  1809. pPlayer->m_Shared.RemoveCond( TF_COND_MARKEDFORDEATH );
  1810. }
  1811. m_nSkin = m_nSkin - TF_FLAG_NUMBEROFSKINS;
  1812. RemoveFlagTrail();
  1813. int nMaxReturnTime = GetMaxReturnTime();
  1814. SetFlagReturnIn( GetReturnTime( nMaxReturnTime ), nMaxReturnTime );
  1815. // Reset the flag's angles.
  1816. SetAbsAngles( m_vecResetAng );
  1817. // Reset the touch function.
  1818. SetTouch( &CCaptureFlag::FlagTouch );
  1819. SetFlagStatus( TF_FLAGINFO_DROPPED );
  1820. // Output.
  1821. m_outputOnDrop.FireOutput( this, this );
  1822. if ( !TFGameRules()->IsMannVsMachineMode() || ( GetMaxReturnTime() < 600 ) )
  1823. {
  1824. CreateReturnIcon();
  1825. }
  1826. // did we get dropped in a func_respawnflag zone?
  1827. if ( PointInRespawnFlagZone( GetAbsOrigin() ) == true )
  1828. {
  1829. Reset();
  1830. ResetMessage();
  1831. }
  1832. HandleFlagDroppedInDetectionZone( pPlayer );
  1833. // update the objective resource so clients have the information
  1834. if ( TFObjectiveResource() )
  1835. {
  1836. TFObjectiveResource()->SetFlagCarrierUpgradeLevel( 0 );
  1837. TFObjectiveResource()->SetBaseMvMBombUpgradeTime( -1 );
  1838. TFObjectiveResource()->SetNextMvMBombUpgradeTime( -1 );
  1839. }
  1840. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  1841. {
  1842. TFGameRules()->HaveAllPlayersSpeakConceptIfAllowed( MP_CONCEPT_MVM_BOMB_DROPPED, TF_TEAM_PVE_DEFENDERS );
  1843. }
  1844. #endif
  1845. }
  1846. //-----------------------------------------------------------------------------
  1847. // Purpose:
  1848. //-----------------------------------------------------------------------------
  1849. bool CCaptureFlag::IsDropped( void )
  1850. {
  1851. return ( m_nFlagStatus == TF_FLAGINFO_DROPPED );
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. // Purpose:
  1855. //-----------------------------------------------------------------------------
  1856. bool CCaptureFlag::IsHome( void )
  1857. {
  1858. return ( m_nFlagStatus == TF_FLAGINFO_HOME );
  1859. }
  1860. //-----------------------------------------------------------------------------
  1861. // Purpose:
  1862. //-----------------------------------------------------------------------------
  1863. bool CCaptureFlag::IsStolen( void )
  1864. {
  1865. return ( m_nFlagStatus == TF_FLAGINFO_STOLEN );
  1866. }
  1867. //-----------------------------------------------------------------------------
  1868. // Purpose:
  1869. //-----------------------------------------------------------------------------
  1870. bool CCaptureFlag::IsDisabled( void ) const
  1871. {
  1872. return m_bDisabled;
  1873. }
  1874. //-----------------------------------------------------------------------------
  1875. // Purpose:
  1876. //-----------------------------------------------------------------------------
  1877. void CCaptureFlag::SetDisabled( bool bDisabled )
  1878. {
  1879. m_bDisabled = bDisabled;
  1880. if ( bDisabled )
  1881. {
  1882. if ( m_bVisibleWhenDisabled )
  1883. {
  1884. SetRenderMode( kRenderTransAlpha );
  1885. SetRenderColorA( 180 );
  1886. RemoveEffects( EF_NODRAW );
  1887. }
  1888. else
  1889. {
  1890. AddEffects( EF_NODRAW );
  1891. }
  1892. SetTouch( NULL );
  1893. SetThink( NULL );
  1894. }
  1895. else
  1896. {
  1897. RemoveEffects( EF_NODRAW );
  1898. SetRenderMode( kRenderNormal );
  1899. SetRenderColorA( 255 );
  1900. // The flag in RD is not actually touched by players when it's home
  1901. SetTouch( &CCaptureFlag::FlagTouch );
  1902. SetThink( &CCaptureFlag::Think );
  1903. SetNextThink( gpGlobals->curtime );
  1904. #ifdef GAME_DLL
  1905. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) && ( GetTeamNumber() == TEAM_UNASSIGNED ) )
  1906. {
  1907. TFGameRules()->StartDoomsdayTicketsTimer();
  1908. }
  1909. #endif
  1910. }
  1911. #ifdef CLIENT_DLL
  1912. UpdateGlowEffect();
  1913. #endif
  1914. }
  1915. void CCaptureFlag::SetVisibleWhenDisabled( bool bVisible )
  1916. {
  1917. m_bVisibleWhenDisabled = bVisible;
  1918. SetDisabled( IsDisabled() );
  1919. }
  1920. //-----------------------------------------------------------------------------
  1921. // Purpose: Sets the flag status
  1922. //-----------------------------------------------------------------------------
  1923. void CCaptureFlag::SetFlagStatus( int iStatus, CBasePlayer *pNewOwner /*= NULL*/ )
  1924. {
  1925. #ifdef GAME_DLL
  1926. MDLCACHE_CRITICAL_SECTION();
  1927. #endif
  1928. if ( m_nFlagStatus != iStatus )
  1929. {
  1930. m_nFlagStatus = iStatus;
  1931. IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
  1932. if ( pEvent )
  1933. {
  1934. #ifdef GAME_DLL
  1935. pEvent->SetInt( "userid", pNewOwner ? pNewOwner->GetUserID() : -1 );
  1936. pEvent->SetInt( "entindex", entindex() );
  1937. #endif
  1938. gameeventmanager->FireEvent( pEvent );
  1939. }
  1940. }
  1941. #ifdef CLIENT_DLL
  1942. UpdateGlowEffect();
  1943. #endif
  1944. #ifdef GAME_DLL
  1945. switch ( m_nFlagStatus )
  1946. {
  1947. case TF_FLAGINFO_HOME:
  1948. case TF_FLAGINFO_DROPPED:
  1949. ResetSequence( LookupSequence("spin") ); // set spin animation if it's not being held
  1950. break;
  1951. case TF_FLAGINFO_STOLEN:
  1952. ResetSequence( LookupSequence("idle") ); // set idle animation if it is being held
  1953. break;
  1954. default:
  1955. AssertOnce( false ); // invalid stats
  1956. break;
  1957. }
  1958. #endif
  1959. }
  1960. //-----------------------------------------------------------------------------------------------
  1961. // GAME DLL Functions
  1962. //-----------------------------------------------------------------------------------------------
  1963. #ifdef GAME_DLL
  1964. //-----------------------------------------------------------------------------
  1965. // Purpose:
  1966. //-----------------------------------------------------------------------------
  1967. void CCaptureFlag::Think( void )
  1968. {
  1969. // Is the flag enabled?
  1970. if ( IsDisabled() )
  1971. return;
  1972. if ( !TFGameRules()->FlagsMayBeCapped() )
  1973. {
  1974. SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
  1975. return;
  1976. }
  1977. if ( m_bCaptured )
  1978. {
  1979. m_bCaptured = false;
  1980. SetTouch( &CCaptureFlag::FlagTouch );
  1981. }
  1982. if ( IsDropped() )
  1983. {
  1984. if ( !m_bAllowOwnerPickup )
  1985. {
  1986. if ( m_flOwnerPickupTime && gpGlobals->curtime > m_flOwnerPickupTime )
  1987. {
  1988. m_bAllowOwnerPickup = true;
  1989. }
  1990. }
  1991. if ( TFGameRules()->IsMannVsMachineMode() && m_bReturnBetweenWaves )
  1992. {
  1993. if ( TFGameRules()->InSetup() || ( TFObjectiveResource() && TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() ) )
  1994. {
  1995. Reset();
  1996. }
  1997. else if ( g_pPopulationManager && g_pPopulationManager->IsInEndlessWaves() && g_pPopulationManager->EndlessShouldResetFlag() )
  1998. {
  1999. Reset();
  2000. g_pPopulationManager->EndlessFlagHasReset();
  2001. ResetMessage();
  2002. }
  2003. }
  2004. if ( m_nType == TF_FLAGTYPE_INVADE )
  2005. {
  2006. if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
  2007. {
  2008. Reset();
  2009. ResetMessage();
  2010. }
  2011. else if ( m_flNeutralTime && gpGlobals->curtime > m_flNeutralTime )
  2012. {
  2013. // reset the team to the original team setting (when it spawned)
  2014. ChangeTeam( m_iOriginalTeam );
  2015. m_nSkin = ( GetTeamNumber() == TEAM_UNASSIGNED ) ? 2 : (GetTeamNumber() - 2);
  2016. ResetFlagNeutralTime();
  2017. }
  2018. }
  2019. else
  2020. {
  2021. if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
  2022. {
  2023. Reset();
  2024. ResetMessage();
  2025. }
  2026. }
  2027. }
  2028. else if ( IsStolen() && m_hPrevOwner )
  2029. {
  2030. CBasePlayer *pPlayer = ToBasePlayer( m_hPrevOwner );
  2031. if ( pPlayer )
  2032. {
  2033. pPlayer->SetLastObjectiveTime( gpGlobals->curtime );
  2034. }
  2035. }
  2036. if ( m_flResetTime && gpGlobals->curtime > m_flResetTime )
  2037. {
  2038. DestroyReturnIcon();
  2039. }
  2040. CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
  2041. if ( pPlayer )
  2042. {
  2043. bool bRunning;
  2044. float flSpeed = pPlayer->MaxSpeed();
  2045. flSpeed *= flSpeed;
  2046. if ( pPlayer->GetAbsVelocity().LengthSqr() >= (flSpeed* 0.1f) )
  2047. {
  2048. bRunning = true;
  2049. }
  2050. else
  2051. {
  2052. bRunning = false;
  2053. }
  2054. if ( !bRunning && m_pFlagTrail )
  2055. {
  2056. RemoveFlagTrail();
  2057. }
  2058. else if ( bRunning && !m_pFlagTrail )
  2059. {
  2060. StartFlagTrail();
  2061. }
  2062. }
  2063. if ( m_nType == TF_FLAGTYPE_RESOURCE_CONTROL )
  2064. {
  2065. if ( TFGameRules() && TFGameRules()->IsHalloweenScenario( CTFGameRules::HALLOWEEN_SCENARIO_DOOMSDAY ) )
  2066. {
  2067. if ( TFGameRules()->DoomsdayTicketTimerElapsed() )
  2068. {
  2069. if ( CTFMinigameLogic::GetMinigameLogic() && CTFMinigameLogic::GetMinigameLogic()->GetActiveMinigame() )
  2070. {
  2071. // we've started playing a minigame so just cancel the timer
  2072. TFGameRules()->StopDoomsdayTicketsTimer();
  2073. }
  2074. else
  2075. {
  2076. TFGameRules()->StartDoomsdayTicketsTimer(); // start the timer again
  2077. TFGameRules()->BroadcastSound( 255, TF_RESOURCE_EVENT_NAGS );
  2078. }
  2079. }
  2080. }
  2081. }
  2082. if ( IsStolen() && TFGameRules() && TFGameRules()->IsPowerupMode() && IsPoisonous() && !pPlayer->m_Shared.InCond( TF_COND_MARKEDFORDEATH ) )
  2083. {
  2084. pPlayer->m_Shared.AddCond( TF_COND_MARKEDFORDEATH );
  2085. }
  2086. SetNextThink( gpGlobals->curtime + TF_FLAG_THINK_TIME );
  2087. }
  2088. void CCaptureFlag::CreateReturnIcon( void )
  2089. {
  2090. if ( m_hReturnIcon.Get() )
  2091. return;
  2092. CBaseEntity *pReturnIcon = CBaseEntity::Create( "item_teamflag_return_icon", GetAbsOrigin() + Vector(0,0,cl_flag_return_height.GetFloat()), vec3_angle, this );
  2093. if ( pReturnIcon )
  2094. {
  2095. m_hReturnIcon = pReturnIcon;
  2096. m_hReturnIcon->SetParent( this );
  2097. }
  2098. }
  2099. void CCaptureFlag::DestroyReturnIcon( void )
  2100. {
  2101. if ( !m_hReturnIcon.Get() )
  2102. return;
  2103. UTIL_Remove( m_hReturnIcon );
  2104. m_hReturnIcon = NULL;
  2105. }
  2106. //-----------------------------------------------------------------------------
  2107. // Purpose:
  2108. //-----------------------------------------------------------------------------
  2109. void CCaptureFlag::InputEnable( inputdata_t &inputdata )
  2110. {
  2111. SetDisabled( false );
  2112. }
  2113. //-----------------------------------------------------------------------------
  2114. // Purpose:
  2115. //-----------------------------------------------------------------------------
  2116. void CCaptureFlag::InputDisable( inputdata_t &inputdata )
  2117. {
  2118. SetDisabled( true );
  2119. }
  2120. //-----------------------------------------------------------------------------
  2121. // Purpose:
  2122. //-----------------------------------------------------------------------------
  2123. void CCaptureFlag::InputRoundActivate( inputdata_t &inputdata )
  2124. {
  2125. CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
  2126. // If the player has a capture flag, drop it.
  2127. if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
  2128. {
  2129. Drop( pPlayer, true, false, false );
  2130. }
  2131. Reset();
  2132. }
  2133. //-----------------------------------------------------------------------------
  2134. // Purpose:
  2135. //-----------------------------------------------------------------------------
  2136. void CCaptureFlag::InputForceDrop( inputdata_t &inputdata )
  2137. {
  2138. CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
  2139. // If the player has a capture flag, drop it.
  2140. if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
  2141. {
  2142. pPlayer->DropFlag();
  2143. }
  2144. }
  2145. //-----------------------------------------------------------------------------
  2146. // Purpose:
  2147. //-----------------------------------------------------------------------------
  2148. void CCaptureFlag::InternalForceReset( bool bSilent /* = false */ )
  2149. {
  2150. if ( IsHome() )
  2151. return;
  2152. CTFPlayer *pPlayer = ToTFPlayer( m_hPrevOwner.Get() );
  2153. // If the player has a capture flag, drop it.
  2154. if ( pPlayer && pPlayer->HasItem() && ( pPlayer->GetItem() == this ) )
  2155. {
  2156. pPlayer->DropFlag( bSilent );
  2157. }
  2158. if ( !bSilent )
  2159. {
  2160. ResetFlag();
  2161. }
  2162. else
  2163. {
  2164. Reset();
  2165. }
  2166. }
  2167. //-----------------------------------------------------------------------------
  2168. // Purpose:
  2169. //-----------------------------------------------------------------------------
  2170. void CCaptureFlag::InputForceReset( inputdata_t &inputdata )
  2171. {
  2172. InternalForceReset();
  2173. }
  2174. //-----------------------------------------------------------------------------
  2175. // Purpose:
  2176. //-----------------------------------------------------------------------------
  2177. void CCaptureFlag::InputForceResetSilent( inputdata_t &inputdata )
  2178. {
  2179. InternalForceReset( true );
  2180. }
  2181. void CCaptureFlag::InputForceResetAndDisableSilent( inputdata_t &inputdata )
  2182. {
  2183. InternalForceReset( true );
  2184. SetDisabled( true );
  2185. }
  2186. //-----------------------------------------------------------------------------
  2187. // Purpose:
  2188. //-----------------------------------------------------------------------------
  2189. void CCaptureFlag::InputSetReturnTime( inputdata_t &inputdata )
  2190. {
  2191. int nReturnTime = inputdata.value.Int();
  2192. m_nReturnTime = ( nReturnTime >= 0 ) ? nReturnTime : 0;
  2193. if ( IsDropped() )
  2194. {
  2195. // do we currently have a neutral time?
  2196. if ( m_flNeutralTime > 0 )
  2197. {
  2198. if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_HALF )
  2199. {
  2200. SetFlagNeutralIn( (float)m_nReturnTime / 2.0 );
  2201. }
  2202. else if ( m_nNeutralType == INVADE_NEUTRAL_TYPE_DEFAULT )
  2203. {
  2204. // if our return time is less than the neutral time, we don't need a neutral time
  2205. if ( TF_INVADE_NEUTRAL_TIME < m_nReturnTime )
  2206. {
  2207. SetFlagNeutralIn( TF_INVADE_NEUTRAL_TIME );
  2208. }
  2209. }
  2210. }
  2211. SetFlagReturnIn( m_nReturnTime );
  2212. }
  2213. }
  2214. void CCaptureFlag::InputShowTimer( inputdata_t &inputdata )
  2215. {
  2216. int nReturnTime = inputdata.value.Int();
  2217. m_nReturnTime = ( nReturnTime >= 0 ) ? nReturnTime : 0;
  2218. SetFlagReturnIn( m_nReturnTime );
  2219. CreateReturnIcon();
  2220. }
  2221. void CCaptureFlag::InputForceGlowDisabled( inputdata_t &inputdata )
  2222. {
  2223. int nState = inputdata.value.Int();
  2224. SetGlowEnabled( nState == 0 );
  2225. }
  2226. //-----------------------------------------------------------------------------
  2227. // Purpose: Always transmitted to clients
  2228. //-----------------------------------------------------------------------------
  2229. int CCaptureFlag::UpdateTransmitState()
  2230. {
  2231. // ALWAYS transmit to all clients.
  2232. return SetTransmitState( FL_EDICT_ALWAYS );
  2233. }
  2234. #else
  2235. float CCaptureFlag::GetReturnProgress()
  2236. {
  2237. float flEventTime = MAX( m_flResetTime.m_Value, m_flNeutralTime.m_Value );
  2238. return ( 1.0 - ( ( flEventTime - gpGlobals->curtime ) / m_flMaxResetTime ) );
  2239. }
  2240. void CCaptureFlag::Simulate( void )
  2241. {
  2242. BaseClass::Simulate();
  2243. ManageTrailEffects();
  2244. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  2245. if ( m_hPrevOwner && m_hPrevOwner->IsPlayer() && pLocalPlayer && pLocalPlayer->m_Shared.IsFullyInvisible() && !IsEffectActive( EF_NODRAW ) )
  2246. {
  2247. C_TFPlayer *pTFOwner = ToTFPlayer( m_hPrevOwner );
  2248. if ( pTFOwner && pTFOwner != pLocalPlayer )
  2249. {
  2250. AddEffects( EF_NODRAW );
  2251. }
  2252. }
  2253. else if ( IsEffectActive( EF_NODRAW ) && ( IsStolen() || IsDropped() ) )
  2254. {
  2255. RemoveEffects( EF_NODRAW );
  2256. }
  2257. }
  2258. void CCaptureFlag::ManageTrailEffects( void )
  2259. {
  2260. if ( ( m_nUseTrailEffect == FLAG_EFFECTS_NONE ) || ( m_nUseTrailEffect == FLAG_EFFECTS_COLORONLY ) )
  2261. return;
  2262. if ( m_nFlagStatus == TF_FLAGINFO_STOLEN )
  2263. {
  2264. if ( GetPrevOwner() )
  2265. {
  2266. CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
  2267. if ( pPlayer )
  2268. {
  2269. if ( pPlayer->GetAbsVelocity().Length() >= pPlayer->MaxSpeed() * 0.2f )
  2270. {
  2271. if ( m_pPaperTrailEffect == NULL )
  2272. {
  2273. if ( !( TFGameRules() && TFGameRules()->IsPVEModeActive() ) )
  2274. {
  2275. m_pPaperTrailEffect = ParticleProp()->Create( GetPaperEffect(), PATTACH_ABSORIGIN_FOLLOW );
  2276. }
  2277. }
  2278. }
  2279. else
  2280. {
  2281. if ( m_pPaperTrailEffect )
  2282. {
  2283. ParticleProp()->StopEmission( m_pPaperTrailEffect );
  2284. m_pPaperTrailEffect = NULL;
  2285. }
  2286. }
  2287. }
  2288. }
  2289. }
  2290. else
  2291. {
  2292. if ( m_pPaperTrailEffect )
  2293. {
  2294. ParticleProp()->StopEmission( m_pPaperTrailEffect );
  2295. m_pPaperTrailEffect = NULL;
  2296. }
  2297. }
  2298. }
  2299. #endif
  2300. LINK_ENTITY_TO_CLASS( item_teamflag_return_icon, CCaptureFlagReturnIcon );
  2301. IMPLEMENT_NETWORKCLASS_ALIASED( CaptureFlagReturnIcon, DT_CaptureFlagReturnIcon )
  2302. BEGIN_NETWORK_TABLE( CCaptureFlagReturnIcon, DT_CaptureFlagReturnIcon )
  2303. END_NETWORK_TABLE()
  2304. CCaptureFlagReturnIcon::CCaptureFlagReturnIcon()
  2305. {
  2306. #ifdef CLIENT_DLL
  2307. m_pReturnProgressMaterial_Empty = NULL;
  2308. m_pReturnProgressMaterial_Full = NULL;
  2309. #endif
  2310. }
  2311. #ifdef GAME_DLL
  2312. void CCaptureFlagReturnIcon::Spawn( void )
  2313. {
  2314. BaseClass::Spawn();
  2315. UTIL_SetSize( this, Vector(-8,-8,-8), Vector(8,8,8) );
  2316. CollisionProp()->SetCollisionBounds( Vector( -50, -50, -50 ), Vector( 50, 50, 50 ) );
  2317. }
  2318. int CCaptureFlagReturnIcon::UpdateTransmitState( void )
  2319. {
  2320. return SetTransmitState( FL_EDICT_ALWAYS );
  2321. }
  2322. //-----------------------------------------------------------------------------
  2323. // Purpose: Start the flag trail
  2324. //-----------------------------------------------------------------------------
  2325. void CCaptureFlag::StartFlagTrail( void )
  2326. {
  2327. if ( ( m_nUseTrailEffect == FLAG_EFFECTS_NONE ) || ( m_nUseTrailEffect == FLAG_EFFECTS_PAPERONLY ) )
  2328. return;
  2329. if ( m_pFlagTrail )
  2330. return;
  2331. CTFPlayer *pPlayer = ToTFPlayer( GetPrevOwner() );
  2332. if ( pPlayer )
  2333. {
  2334. if ( !m_pFlagTrail )
  2335. {
  2336. char szTrailTeamName[ MAX_PATH ];
  2337. GetTrailEffect( pPlayer->GetTeamNumber(), szTrailTeamName, sizeof( szTrailTeamName ) );
  2338. CSpriteTrail *pTempTrail = NULL;
  2339. pTempTrail = CSpriteTrail::SpriteTrailCreate( szTrailTeamName, GetAbsOrigin(), true );
  2340. pTempTrail->SetTransmit( false );
  2341. pTempTrail->FollowEntity( this );
  2342. pTempTrail->SetTransparency( kRenderTransAlpha, 255, 255, 255, TF_FLAG_TRAIL_ALPHA, kRenderFxNone );
  2343. pTempTrail->SetStartWidth( 32 );
  2344. pTempTrail->SetTextureResolution( 1.0f / ( 96.0f * 1.0f ) );
  2345. pTempTrail->SetLifeTime( 0.70 );
  2346. pTempTrail->TurnOn();
  2347. pTempTrail->SetAttachment( this, 0 );
  2348. m_pFlagTrail = pTempTrail;
  2349. }
  2350. }
  2351. }
  2352. //-----------------------------------------------------------------------------
  2353. // Purpose: Fade and kill the trail
  2354. //-----------------------------------------------------------------------------
  2355. void CCaptureFlag::RemoveFlagTrail( void )
  2356. {
  2357. if ( !m_pFlagTrail )
  2358. return;
  2359. if (m_pFlagTrail)
  2360. {
  2361. if (m_flFlagTrailLife <= 0 || m_bInstantTrailRemove == true )
  2362. {
  2363. UTIL_Remove( m_pFlagTrail);
  2364. m_flFlagTrailLife = 1.0f;
  2365. }
  2366. else
  2367. {
  2368. float fAlpha = TF_FLAG_TRAIL_ALPHA * m_flFlagTrailLife;
  2369. CSpriteTrail *pTempTrail = dynamic_cast< CSpriteTrail*>( m_pFlagTrail.Get() );
  2370. if ( pTempTrail )
  2371. {
  2372. pTempTrail->SetBrightness( int(fAlpha) );
  2373. }
  2374. m_flFlagTrailLife = m_flFlagTrailLife - 0.1f;
  2375. SetContextThink( &CCaptureFlag::RemoveFlagTrail, gpGlobals->curtime + 0.05, "FadeFlagTrail");
  2376. }
  2377. }
  2378. m_bInstantTrailRemove = false;
  2379. }
  2380. //-----------------------------------------------------------------------------
  2381. // Purpose:
  2382. //-----------------------------------------------------------------------------
  2383. void CCaptureFlag::AddFollower( CTFBot* pBot )
  2384. {
  2385. if ( !m_followers.HasElement( pBot ) )
  2386. {
  2387. m_followers.AddToTail( pBot );
  2388. for ( int i=0; i<m_tags.Count(); ++i )
  2389. {
  2390. pBot->AddTag( m_tags[i] );
  2391. }
  2392. }
  2393. }
  2394. //-----------------------------------------------------------------------------
  2395. // Purpose:
  2396. //-----------------------------------------------------------------------------
  2397. void CCaptureFlag::RemoveFollower( CTFBot* pBot )
  2398. {
  2399. int index = m_followers.Find( pBot );
  2400. if ( index != m_followers.InvalidIndex() )
  2401. {
  2402. m_followers.Remove( index );
  2403. for ( int i=0; i<m_tags.Count(); ++i )
  2404. {
  2405. pBot->RemoveTag( m_tags[i] );
  2406. }
  2407. }
  2408. }
  2409. //-----------------------------------------------------------------------------
  2410. // Purpose:
  2411. //-----------------------------------------------------------------------------
  2412. int CCaptureFlag::GetReturnTime( int nMaxReturnTime )
  2413. {
  2414. return GetReturnTimeShotClockMode( nMaxReturnTime );
  2415. }
  2416. //-----------------------------------------------------------------------------
  2417. // Purpose:
  2418. //-----------------------------------------------------------------------------
  2419. int CCaptureFlag::GetMaxReturnTime( void )
  2420. {
  2421. int nReturnTime = m_nReturnTime;
  2422. #ifdef STAGING_ONLY
  2423. if (tf_flag_return_time_override.GetInt() > 0)
  2424. {
  2425. nReturnTime = tf_flag_return_time_override.GetInt();
  2426. }
  2427. #endif // STAGING_ONLY
  2428. if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION )
  2429. {
  2430. int nMaxPoints = 300;
  2431. if ( CTFRobotDestructionLogic::GetRobotDestructionLogic() )
  2432. {
  2433. nMaxPoints = CTFRobotDestructionLogic::GetRobotDestructionLogic()->GetMaxPoints();
  2434. }
  2435. const int nMaxReturnTimePoints = nMaxPoints / 3;
  2436. nReturnTime = RemapValClamped(m_nPointValue, 0.f, nMaxReturnTimePoints, tf_rd_return_min_time.GetFloat(), tf_rd_return_max_time.GetFloat());
  2437. }
  2438. else if ( m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  2439. {
  2440. if ( CTFPlayerDestructionLogic::GetPlayerDestructionLogic() )
  2441. {
  2442. nReturnTime = CTFPlayerDestructionLogic::GetPlayerDestructionLogic()->GetFlagResetDelay();
  2443. }
  2444. }
  2445. return nReturnTime;
  2446. }
  2447. //-----------------------------------------------------------------------------
  2448. // Purpose:
  2449. //-----------------------------------------------------------------------------
  2450. void CCaptureFlag::AddPointValue( int nPoints )
  2451. {
  2452. m_nPointValue += nPoints;
  2453. if ( m_nType == TF_FLAGTYPE_ROBOT_DESTRUCTION || m_nType == TF_FLAGTYPE_PLAYER_DESTRUCTION )
  2454. {
  2455. IGameEvent *pEvent = gameeventmanager->CreateEvent( "flagstatus_update" );
  2456. if ( pEvent )
  2457. {
  2458. pEvent->SetInt( "userid", m_hPrevOwner && m_hPrevOwner->IsPlayer() ? ToBasePlayer( m_hPrevOwner )->GetUserID() : -1 );
  2459. pEvent->SetInt( "entindex", entindex() );
  2460. gameeventmanager->FireEvent( pEvent );
  2461. // The return time is determined by how many points are in the flag, so update that.
  2462. m_flLastResetDuration = GetMaxReturnTime();
  2463. }
  2464. if ( nPoints > 0 )
  2465. {
  2466. pEvent = gameeventmanager->CreateEvent( "teamplay_flag_event" );
  2467. if ( pEvent )
  2468. {
  2469. pEvent->SetInt( "player", m_hPrevOwner && m_hPrevOwner->IsPlayer() ? ToBasePlayer( m_hPrevOwner )->entindex() : -1 );
  2470. pEvent->SetInt( "eventtype", TF_FLAGEVENT_PICKUP );
  2471. gameeventmanager->FireEvent( pEvent );
  2472. }
  2473. }
  2474. }
  2475. }
  2476. #endif // GAME_DLL
  2477. #ifdef CLIENT_DLL
  2478. typedef struct
  2479. {
  2480. float maxProgress;
  2481. float vert1x;
  2482. float vert1y;
  2483. float vert2x;
  2484. float vert2y;
  2485. int swipe_dir_x;
  2486. int swipe_dir_y;
  2487. } progress_segment_t;
  2488. // This defines the properties of the 8 circle segments
  2489. // in the circular progress bar.
  2490. progress_segment_t Segments[8] =
  2491. {
  2492. { 0.125, 0.5, 0.0, 1.0, 0.0, 1, 0 },
  2493. { 0.25, 1.0, 0.0, 1.0, 0.5, 0, 1 },
  2494. { 0.375, 1.0, 0.5, 1.0, 1.0, 0, 1 },
  2495. { 0.50, 1.0, 1.0, 0.5, 1.0, -1, 0 },
  2496. { 0.625, 0.5, 1.0, 0.0, 1.0, -1, 0 },
  2497. { 0.75, 0.0, 1.0, 0.0, 0.5, 0, -1 },
  2498. { 0.875, 0.0, 0.5, 0.0, 0.0, 0, -1 },
  2499. { 1.0, 0.0, 0.0, 0.5, 0.0, 1, 0 },
  2500. };
  2501. //-----------------------------------------------------------------------------
  2502. // Purpose:
  2503. //-----------------------------------------------------------------------------
  2504. RenderGroup_t CCaptureFlagReturnIcon::GetRenderGroup( void )
  2505. {
  2506. return RENDER_GROUP_TRANSLUCENT_ENTITY;
  2507. }
  2508. //-----------------------------------------------------------------------------
  2509. // Purpose:
  2510. //-----------------------------------------------------------------------------
  2511. void CCaptureFlagReturnIcon::GetRenderBounds( Vector& theMins, Vector& theMaxs )
  2512. {
  2513. theMins.Init( -20, -20, -20 );
  2514. theMaxs.Init( 20, 20, 20 );
  2515. }
  2516. //-----------------------------------------------------------------------------
  2517. // Purpose:
  2518. //-----------------------------------------------------------------------------
  2519. int CCaptureFlagReturnIcon::DrawModel( int flags )
  2520. {
  2521. int nRetVal = BaseClass::DrawModel( flags );
  2522. DrawReturnProgressBar();
  2523. return nRetVal;
  2524. }
  2525. //-----------------------------------------------------------------------------
  2526. // Purpose: Draw progress bar above the flag indicating when it will return
  2527. //-----------------------------------------------------------------------------
  2528. void CCaptureFlagReturnIcon::DrawReturnProgressBar( void )
  2529. {
  2530. CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag * > ( GetOwnerEntity() );
  2531. if ( !pFlag )
  2532. return;
  2533. // Don't draw if this flag is not going to reset
  2534. if ( pFlag->GetMaxResetTime() <= 0 )
  2535. return;
  2536. if ( !TFGameRules()->FlagsMayBeCapped() )
  2537. return;
  2538. if ( !m_pReturnProgressMaterial_Full )
  2539. {
  2540. m_pReturnProgressMaterial_Full = materials->FindMaterial( "VGUI/flagtime_full", TEXTURE_GROUP_VGUI );
  2541. }
  2542. if ( !m_pReturnProgressMaterial_Empty )
  2543. {
  2544. m_pReturnProgressMaterial_Empty = materials->FindMaterial( "VGUI/flagtime_empty", TEXTURE_GROUP_VGUI );
  2545. }
  2546. if ( !m_pReturnProgressMaterial_Full || !m_pReturnProgressMaterial_Empty )
  2547. {
  2548. return;
  2549. }
  2550. CMatRenderContextPtr pRenderContext( materials );
  2551. Vector vOrigin = GetAbsOrigin();
  2552. QAngle vAngle = vec3_angle;
  2553. // Align it towards the viewer
  2554. Vector vUp = CurrentViewUp();
  2555. Vector vRight = CurrentViewRight();
  2556. if ( fabs( vRight.z ) > 0.95 ) // don't draw it edge-on
  2557. return;
  2558. vRight.z = 0;
  2559. VectorNormalize( vRight );
  2560. float flSize = cl_flag_return_size.GetFloat();
  2561. unsigned char ubColor[4];
  2562. ubColor[3] = 255;
  2563. switch( pFlag->GetTeamNumber() )
  2564. {
  2565. case TF_TEAM_RED:
  2566. ubColor[0] = 255;
  2567. ubColor[1] = 0;
  2568. ubColor[2] = 0;
  2569. break;
  2570. case TF_TEAM_BLUE:
  2571. ubColor[0] = 0;
  2572. ubColor[1] = 0;
  2573. ubColor[2] = 255;
  2574. break;
  2575. default:
  2576. ubColor[0] = 200;
  2577. ubColor[1] = 200;
  2578. ubColor[2] = 200;
  2579. break;
  2580. }
  2581. // First we draw a quad of a complete icon, background
  2582. CMeshBuilder meshBuilder;
  2583. pRenderContext->Bind( m_pReturnProgressMaterial_Empty );
  2584. IMesh *pMesh = pRenderContext->GetDynamicMesh();
  2585. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  2586. meshBuilder.Color4ubv( ubColor );
  2587. meshBuilder.TexCoord2f( 0,0,0 );
  2588. meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * flSize)).Base() );
  2589. meshBuilder.AdvanceVertex();
  2590. meshBuilder.Color4ubv( ubColor );
  2591. meshBuilder.TexCoord2f( 0,1,0 );
  2592. meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * flSize)).Base() );
  2593. meshBuilder.AdvanceVertex();
  2594. meshBuilder.Color4ubv( ubColor );
  2595. meshBuilder.TexCoord2f( 0,1,1 );
  2596. meshBuilder.Position3fv( (vOrigin + (vRight * flSize) + (vUp * -flSize)).Base() );
  2597. meshBuilder.AdvanceVertex();
  2598. meshBuilder.Color4ubv( ubColor );
  2599. meshBuilder.TexCoord2f( 0,0,1 );
  2600. meshBuilder.Position3fv( (vOrigin + (vRight * -flSize) + (vUp * -flSize)).Base() );
  2601. meshBuilder.AdvanceVertex();
  2602. meshBuilder.End();
  2603. pMesh->Draw();
  2604. float flProgress = pFlag->GetReturnProgress();
  2605. pRenderContext->Bind( m_pReturnProgressMaterial_Full );
  2606. pMesh = pRenderContext->GetDynamicMesh();
  2607. vRight *= flSize * 2;
  2608. vUp *= flSize * -2;
  2609. // Next we're drawing the circular progress bar, in 8 segments
  2610. // For each segment, we calculate the vertex position that will draw
  2611. // the slice.
  2612. int i;
  2613. for ( i=0;i<8;i++ )
  2614. {
  2615. if ( flProgress < Segments[i].maxProgress )
  2616. {
  2617. CMeshBuilder meshBuilder_Full;
  2618. meshBuilder_Full.Begin( pMesh, MATERIAL_TRIANGLES, 3 );
  2619. // vert 0 is ( 0.5, 0.5 )
  2620. meshBuilder_Full.Color4ubv( ubColor );
  2621. meshBuilder_Full.TexCoord2f( 0, 0.5, 0.5 );
  2622. meshBuilder_Full.Position3fv( vOrigin.Base() );
  2623. meshBuilder_Full.AdvanceVertex();
  2624. // Internal progress is the progress through this particular slice
  2625. float internalProgress = RemapVal( flProgress, Segments[i].maxProgress - 0.125, Segments[i].maxProgress, 0.0, 1.0 );
  2626. internalProgress = clamp( internalProgress, 0.0f, 1.0f );
  2627. // Calculate the x,y of the moving vertex based on internal progress
  2628. float swipe_x = Segments[i].vert2x - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_x;
  2629. float swipe_y = Segments[i].vert2y - ( 1.0 - internalProgress ) * 0.5 * Segments[i].swipe_dir_y;
  2630. // vert 1 is calculated from progress
  2631. meshBuilder_Full.Color4ubv( ubColor );
  2632. meshBuilder_Full.TexCoord2f( 0, swipe_x, swipe_y );
  2633. meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( swipe_x - 0.5 ) ) + (vUp *( swipe_y - 0.5 ) ) ).Base() );
  2634. meshBuilder_Full.AdvanceVertex();
  2635. // vert 2 is ( Segments[i].vert1x, Segments[i].vert1y )
  2636. meshBuilder_Full.Color4ubv( ubColor );
  2637. meshBuilder_Full.TexCoord2f( 0, Segments[i].vert2x, Segments[i].vert2y );
  2638. meshBuilder_Full.Position3fv( (vOrigin + (vRight * ( Segments[i].vert2x - 0.5 ) ) + (vUp *( Segments[i].vert2y - 0.5 ) ) ).Base() );
  2639. meshBuilder_Full.AdvanceVertex();
  2640. meshBuilder_Full.End();
  2641. pMesh->Draw();
  2642. }
  2643. }
  2644. }
  2645. #endif