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.

1099 lines
32 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <vgui_controls/AnimationController.h>
  8. #include <vgui_controls/EditablePanel.h>
  9. #include "iclientmode.h"
  10. #include "tf_time_panel.h"
  11. #include "tf_gamerules.h"
  12. #include "c_tf_team.h"
  13. #include "vgui_controls/ScalableImagePanel.h"
  14. #include "econ_controls.h"
  15. #include "vgui/ISurface.h"
  16. #include "tf_hud_arena_player_count.h"
  17. #include "tf_match_description.h"
  18. #include "tf_matchmaking_shared.h"
  19. using namespace vgui;
  20. extern ConVar tf_hud_show_servertimelimit;
  21. extern ConVar tf_arena_round_time;
  22. void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  23. bool ShouldUseMatchHUD();
  24. DECLARE_BUILD_FACTORY( CTFProgressBar );
  25. //-----------------------------------------------------------------------------
  26. // Purpose:
  27. //-----------------------------------------------------------------------------
  28. CTFProgressBar::CTFProgressBar( Panel *parent, const char *name )
  29. : ImagePanel( parent, name )
  30. {
  31. m_flPercent = 0.0f;
  32. m_iTexture = surface()->DrawGetTextureId( "hud/objectives_timepanel_progressbar" );
  33. if ( m_iTexture == -1 ) // we didn't find it, so create a new one
  34. {
  35. m_iTexture = surface()->CreateNewTextureID();
  36. surface()->DrawSetTextureFile( m_iTexture, "hud/objectives_timepanel_progressbar", true, false );
  37. }
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose:
  41. //-----------------------------------------------------------------------------
  42. void CTFProgressBar::Paint()
  43. {
  44. int wide, tall;
  45. GetSize( wide, tall );
  46. float uv1 = 0.0f, uv2 = 1.0f;
  47. Vector2D uv11( uv1, uv1 );
  48. Vector2D uv21( uv2, uv1 );
  49. Vector2D uv22( uv2, uv2 );
  50. Vector2D uv12( uv1, uv2 );
  51. Vertex_t verts[4];
  52. verts[0].Init( Vector2D( 0, 0 ), uv11 );
  53. verts[1].Init( Vector2D( wide, 0 ), uv21 );
  54. verts[2].Init( Vector2D( wide, tall ), uv22 );
  55. verts[3].Init( Vector2D( 0, tall ), uv12 );
  56. // first, just draw the whole thing inactive.
  57. surface()->DrawSetTexture( m_iTexture );
  58. surface()->DrawSetColor( m_clrInActive );
  59. surface()->DrawTexturedPolygon( 4, verts );
  60. // now, let's calculate the "active" part of the progress bar
  61. if ( m_flPercent < m_flPercentWarning )
  62. {
  63. surface()->DrawSetColor( m_clrActive );
  64. }
  65. else
  66. {
  67. surface()->DrawSetColor( m_clrWarning );
  68. }
  69. // we're going to do this using quadrants
  70. // -------------------------
  71. // | | |
  72. // | | |
  73. // | 4 | 1 |
  74. // | | |
  75. // | | |
  76. // -------------------------
  77. // | | |
  78. // | | |
  79. // | 3 | 2 |
  80. // | | |
  81. // | | |
  82. // -------------------------
  83. float flCompleteCircle = ( 2.0f * M_PI );
  84. float fl90degrees = flCompleteCircle / 4.0f;
  85. float flEndAngle = flCompleteCircle * ( 1.0f - m_flPercent ); // count DOWN (counter-clockwise)
  86. //float flEndAngle = flCompleteCircle * m_flPercent; // count UP (clockwise)
  87. float flHalfWide = (float)wide / 2.0f;
  88. float flHalfTall = (float)tall / 2.0f;
  89. if ( flEndAngle >= fl90degrees * 3.0f ) // >= 270 degrees
  90. {
  91. // draw the first and second quadrants
  92. uv11.Init( 0.5f, 0.0f );
  93. uv21.Init( 1.0f, 0.0f );
  94. uv22.Init( 1.0f, 1.0f );
  95. uv12.Init( 0.5, 1.0f );
  96. verts[0].Init( Vector2D( flHalfWide, 0.0f ), uv11 );
  97. verts[1].Init( Vector2D( wide, 0.0f ), uv21 );
  98. verts[2].Init( Vector2D( wide, tall ), uv22 );
  99. verts[3].Init( Vector2D( flHalfWide, tall ), uv12 );
  100. surface()->DrawTexturedPolygon( 4, verts );
  101. // draw the third quadrant
  102. uv11.Init( 0.0f, 0.5f );
  103. uv21.Init( 0.5f, 0.5f );
  104. uv22.Init( 0.5f, 1.0f );
  105. uv12.Init( 0.0f, 1.0f );
  106. verts[0].Init( Vector2D( 0.0f, flHalfTall ), uv11 );
  107. verts[1].Init( Vector2D( flHalfWide, flHalfTall ), uv21 );
  108. verts[2].Init( Vector2D( flHalfWide, tall ), uv22 );
  109. verts[3].Init( Vector2D( 0.0f, tall ), uv12 );
  110. surface()->DrawTexturedPolygon( 4, verts );
  111. // draw the partial fourth quadrant
  112. if ( flEndAngle > fl90degrees * 3.5f ) // > 315 degrees
  113. {
  114. uv11.Init( 0.0f, 0.0f );
  115. uv21.Init( 0.5f - ( tan(fl90degrees * 4.0f - flEndAngle) * 0.5 ), 0.0f );
  116. uv22.Init( 0.5f, 0.5f );
  117. uv12.Init( 0.0f, 0.5f );
  118. verts[0].Init( Vector2D( 0.0f, 0.0f ), uv11 );
  119. verts[1].Init( Vector2D( flHalfWide - ( tan(fl90degrees * 4.0f - flEndAngle) * flHalfTall ), 0.0f ), uv21 );
  120. verts[2].Init( Vector2D( flHalfWide, flHalfTall ), uv22 );
  121. verts[3].Init( Vector2D( 0.0f, flHalfTall ), uv12 );
  122. surface()->DrawTexturedPolygon( 4, verts );
  123. }
  124. else // <= 315 degrees
  125. {
  126. uv11.Init( 0.0f, 0.5f );
  127. uv21.Init( 0.0f, 0.5f - ( tan(flEndAngle - fl90degrees * 3.0f) * 0.5 ) );
  128. uv22.Init( 0.5f, 0.5f );
  129. uv12.Init( 0.0f, 0.5f );
  130. verts[0].Init( Vector2D( 0.0f, flHalfTall ), uv11 );
  131. verts[1].Init( Vector2D( 0.0f, flHalfTall - ( tan(flEndAngle - fl90degrees * 3.0f) * flHalfWide ) ), uv21 );
  132. verts[2].Init( Vector2D( flHalfWide, flHalfTall ), uv22 );
  133. verts[3].Init( Vector2D( 0.0f, flHalfTall ), uv12 );
  134. surface()->DrawTexturedPolygon( 4, verts );
  135. }
  136. }
  137. else if ( flEndAngle >= fl90degrees * 2.0f ) // >= 180 degrees
  138. {
  139. // draw the first and second quadrants
  140. uv11.Init( 0.5f, 0.0f );
  141. uv21.Init( 1.0f, 0.0f );
  142. uv22.Init( 1.0f, 1.0f );
  143. uv12.Init( 0.5, 1.0f );
  144. verts[0].Init( Vector2D( flHalfWide, 0.0f ), uv11 );
  145. verts[1].Init( Vector2D( wide, 0.0f ), uv21 );
  146. verts[2].Init( Vector2D( wide, tall ), uv22 );
  147. verts[3].Init( Vector2D( flHalfWide, tall ), uv12 );
  148. surface()->DrawTexturedPolygon( 4, verts );
  149. // draw the partial third quadrant
  150. if ( flEndAngle > fl90degrees * 2.5f ) // > 225 degrees
  151. {
  152. uv11.Init( 0.5f, 0.5f );
  153. uv21.Init( 0.5f, 1.0f );
  154. uv22.Init( 0.0f, 1.0f );
  155. uv12.Init( 0.0f, 0.5f + ( tan(fl90degrees * 3.0f - flEndAngle) * 0.5 ) );
  156. verts[0].Init( Vector2D( flHalfWide, flHalfTall ), uv11 );
  157. verts[1].Init( Vector2D( flHalfWide, tall ), uv21 );
  158. verts[2].Init( Vector2D( 0.0f, tall ), uv22 );
  159. verts[3].Init( Vector2D( 0.0f, flHalfTall + ( tan(fl90degrees * 3.0f - flEndAngle) * flHalfWide ) ), uv12 );
  160. surface()->DrawTexturedPolygon( 4, verts );
  161. }
  162. else // <= 225 degrees
  163. {
  164. uv11.Init( 0.5f, 0.5f );
  165. uv21.Init( 0.5f, 1.0f );
  166. uv22.Init( 0.5f - ( tan( flEndAngle - fl90degrees * 2.0f) * 0.5 ), 1.0f );
  167. uv12.Init( 0.5f, 0.5f );
  168. verts[0].Init( Vector2D( flHalfWide, flHalfTall ), uv11 );
  169. verts[1].Init( Vector2D( flHalfWide, tall ), uv21 );
  170. verts[2].Init( Vector2D( flHalfWide - ( tan(flEndAngle - fl90degrees * 2.0f) * flHalfTall ), tall ), uv22 );
  171. verts[3].Init( Vector2D( flHalfWide, flHalfTall ), uv12 );
  172. surface()->DrawTexturedPolygon( 4, verts );
  173. }
  174. }
  175. else if ( flEndAngle >= fl90degrees ) // >= 90 degrees
  176. {
  177. // draw the first quadrant
  178. uv11.Init( 0.5f, 0.0f );
  179. uv21.Init( 1.0f, 0.0f );
  180. uv22.Init( 1.0f, 0.5f );
  181. uv12.Init( 0.5f, 0.5f );
  182. verts[0].Init( Vector2D( flHalfWide, 0.0f ), uv11 );
  183. verts[1].Init( Vector2D( wide, 0.0f ), uv21 );
  184. verts[2].Init( Vector2D( wide, flHalfTall ), uv22 );
  185. verts[3].Init( Vector2D( flHalfWide, flHalfTall ), uv12 );
  186. surface()->DrawTexturedPolygon( 4, verts );
  187. // draw the partial second quadrant
  188. if ( flEndAngle > fl90degrees * 1.5f ) // > 135 degrees
  189. {
  190. uv11.Init( 0.5f, 0.5f );
  191. uv21.Init( 1.0f, 0.5f );
  192. uv22.Init( 1.0f, 1.0f );
  193. uv12.Init( 0.5f + ( tan(fl90degrees * 2.0f - flEndAngle) * 0.5f ), 1.0f );
  194. verts[0].Init( Vector2D( flHalfWide, flHalfTall ), uv11 );
  195. verts[1].Init( Vector2D( wide, flHalfTall ), uv21 );
  196. verts[2].Init( Vector2D( wide, tall ), uv22 );
  197. verts[3].Init( Vector2D( flHalfWide + ( tan(fl90degrees * 2.0f - flEndAngle) * flHalfTall ), tall ), uv12 );
  198. surface()->DrawTexturedPolygon( 4, verts );
  199. }
  200. else // <= 135 degrees
  201. {
  202. uv11.Init( 0.5f, 0.5f );
  203. uv21.Init( 1.0f, 0.5f );
  204. uv22.Init( 1.0f, 0.5f + ( tan(flEndAngle - fl90degrees) * 0.5f ) );
  205. uv12.Init( 0.5f, 0.5f );
  206. verts[0].Init( Vector2D( flHalfWide, flHalfTall ), uv11 );
  207. verts[1].Init( Vector2D( wide, flHalfTall ), uv21 );
  208. verts[2].Init( Vector2D( wide, flHalfTall + ( tan(flEndAngle - fl90degrees) * flHalfWide ) ), uv22 );
  209. verts[3].Init( Vector2D( flHalfWide, flHalfTall ), uv12 );
  210. surface()->DrawTexturedPolygon( 4, verts );
  211. }
  212. }
  213. else // > 0 degrees
  214. {
  215. if ( flEndAngle > fl90degrees / 2.0f ) // > 45 degrees
  216. {
  217. uv11.Init( 0.5f, 0.0f );
  218. uv21.Init( 1.0f, 0.0f );
  219. uv22.Init( 1.0f, 0.5f - ( tan(fl90degrees - flEndAngle) * 0.5 ) );
  220. uv12.Init( 0.5f, 0.5f );
  221. verts[0].Init( Vector2D( flHalfWide, 0.0f ), uv11 );
  222. verts[1].Init( Vector2D( wide, 0.0f ), uv21 );
  223. verts[2].Init( Vector2D( wide, flHalfTall - ( tan(fl90degrees - flEndAngle) * flHalfWide ) ), uv22 );
  224. verts[3].Init( Vector2D( flHalfWide, flHalfTall ), uv12 );
  225. surface()->DrawTexturedPolygon( 4, verts );
  226. }
  227. else // <= 45 degrees
  228. {
  229. uv11.Init( 0.5f, 0.0f );
  230. uv21.Init( 0.5 + ( tan(flEndAngle) * 0.5 ), 0.0f );
  231. uv22.Init( 0.5f, 0.5f );
  232. uv12.Init( 0.5f, 0.0f );
  233. verts[0].Init( Vector2D( flHalfWide, 0.0f ), uv11 );
  234. verts[1].Init( Vector2D( flHalfWide + ( tan(flEndAngle) * flHalfTall ), 0.0f ), uv21 );
  235. verts[2].Init( Vector2D( flHalfWide, flHalfTall ), uv22 );
  236. verts[3].Init( Vector2D( flHalfWide, 0.0f ), uv12 );
  237. surface()->DrawTexturedPolygon( 4, verts );
  238. }
  239. }
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. //-----------------------------------------------------------------------------
  244. CTFHudTimeStatus::CTFHudTimeStatus( Panel *parent, const char *name ) : EditablePanel( parent, name )
  245. {
  246. m_pTimeValue = new CExLabel( this, "TimePanelValue", L"" );
  247. m_pProgressBar = NULL;
  248. m_pOvertimeLabel = NULL;
  249. m_pOvertimeBG = NULL;
  250. m_pSuddenDeathLabel = NULL;
  251. m_pSuddenDeathBG = NULL;
  252. m_pWaitingForPlayersBG = NULL;
  253. m_pWaitingForPlayersLabel = NULL;
  254. m_pSetupLabel = NULL;
  255. m_pSetupBG = NULL;
  256. m_pTimerBG = NULL;
  257. m_pServerTimeLabel = NULL;
  258. m_pServerTimeLabelBG = NULL;
  259. m_flNextThink = 0.0f;
  260. m_iTimerIndex = 0;
  261. m_iTimerDeltaHead = 0;
  262. for( int i = 0 ; i < NUM_TIMER_DELTA_ITEMS ; i++ )
  263. {
  264. m_TimerDeltaItems[i].m_flDieTime = 0.0f;
  265. }
  266. ListenForGameEvent( "teamplay_update_timer" );
  267. ListenForGameEvent( "teamplay_timer_time_added" );
  268. ListenForGameEvent( "localplayer_changeteam" );
  269. m_nTeam = TEAM_UNASSIGNED;
  270. m_bKothMode = false;
  271. m_bCachedOvertime = false;
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose:
  275. //-----------------------------------------------------------------------------
  276. void CTFHudTimeStatus::SetTeamBackground( void )
  277. {
  278. m_pTimerBG = dynamic_cast<ScalableImagePanel *>( FindChildByName( "TimePanelBG" ) );
  279. if ( m_pTimerBG )
  280. {
  281. if ( TFGameRules() && ( !TFGameRules()->IsInKothMode() || TFGameRules()->IsInWaitingForPlayers() ) )
  282. {
  283. int iTeam = GetLocalPlayerTeam();
  284. if ( iTeam == TF_TEAM_RED )
  285. {
  286. m_pTimerBG->SetImage( "../hud/objectives_timepanel_red_bg" );
  287. }
  288. else
  289. {
  290. m_pTimerBG->SetImage( "../hud/objectives_timepanel_blue_bg" );
  291. }
  292. }
  293. else
  294. {
  295. if ( m_nTeam == TF_TEAM_RED )
  296. {
  297. m_pTimerBG->SetImage( "../hud/objectives_timepanel_red_bg" );
  298. }
  299. else
  300. {
  301. m_pTimerBG->SetImage( "../hud/objectives_timepanel_blue_bg" );
  302. }
  303. }
  304. }
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. //-----------------------------------------------------------------------------
  309. void CTFHudTimeStatus::FireGameEvent( IGameEvent *event )
  310. {
  311. const char *eventName = event->GetName();
  312. if ( !Q_strcmp( eventName, "teamplay_update_timer" ) )
  313. {
  314. if ( TFGameRules() && TFGameRules()->IsInKothMode() )
  315. {
  316. InvalidateLayout( false, true );
  317. }
  318. SetExtraTimePanels();
  319. }
  320. else if ( !Q_strcmp( eventName, "teamplay_timer_time_added" ) )
  321. {
  322. int iIndex = event->GetInt( "timer", -1 );
  323. int nSeconds = event->GetInt( "seconds_added", 0 );
  324. SetTimeAdded( iIndex, nSeconds );
  325. }
  326. else if ( !Q_strcmp( eventName, "localplayer_changeteam" ) )
  327. {
  328. SetTeamBackground();
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose:
  333. //-----------------------------------------------------------------------------
  334. void CTFHudTimeStatus::SetTimeAdded( int iIndex, int nSeconds )
  335. {
  336. if ( m_iTimerIndex != iIndex ) // make sure this is the timer we're displaying in the HUD
  337. return;
  338. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  339. if ( nSeconds != 0 && pPlayer )
  340. {
  341. // create a delta item that floats off the top
  342. timer_delta_t *pNewDeltaItem = &m_TimerDeltaItems[m_iTimerDeltaHead];
  343. m_iTimerDeltaHead++;
  344. m_iTimerDeltaHead %= NUM_TIMER_DELTA_ITEMS;
  345. pNewDeltaItem->m_flDieTime = gpGlobals->curtime + m_flDeltaLifetime;
  346. pNewDeltaItem->m_nAmount = nSeconds;
  347. }
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose:
  351. //-----------------------------------------------------------------------------
  352. void CTFHudTimeStatus::CheckClockLabelLength( CExLabel *pLabel, Panel *pBG )
  353. {
  354. if ( !pLabel || ! pBG )
  355. return;
  356. int textWide, textTall;
  357. pLabel->GetContentSize( textWide, textTall );
  358. // make sure our string isn't longer than the label it's in
  359. if ( textWide > pLabel->GetWide() )
  360. {
  361. int xStart, yStart, wideStart, tallStart;
  362. pLabel->GetBounds( xStart, yStart, wideStart, tallStart );
  363. int newXPos = xStart + ( wideStart / 2.0f ) - ( textWide / 2.0f );
  364. pLabel->SetBounds( newXPos, yStart, textWide, tallStart );
  365. }
  366. // turn off the background if our text label is wider than it is
  367. if ( pLabel->GetWide() > pBG->GetWide() )
  368. {
  369. pBG->SetVisible( false );
  370. }
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose:
  374. //-----------------------------------------------------------------------------
  375. void CTFHudTimeStatus::SetExtraTimePanels()
  376. {
  377. if ( !TFGameRules() )
  378. return;
  379. CTeamRoundTimer *pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( m_iTimerIndex ) );
  380. if ( pTimer && pTimer->IsStopWatchTimer() == true )
  381. {
  382. if( m_pTimerBG )
  383. m_pTimerBG->SetVisible( false );
  384. if( m_pProgressBar )
  385. m_pProgressBar->SetVisible( false );
  386. if( m_pWaitingForPlayersLabel )
  387. m_pWaitingForPlayersLabel->SetVisible( false );
  388. if( m_pWaitingForPlayersBG )
  389. m_pWaitingForPlayersBG->SetVisible( false );
  390. if( m_pOvertimeLabel )
  391. m_pOvertimeLabel->SetVisible( false );
  392. if( m_pOvertimeBG )
  393. m_pOvertimeBG->SetVisible( false );
  394. if( m_pSetupLabel )
  395. m_pSetupLabel->SetVisible( false );
  396. if( m_pSetupBG )
  397. m_pSetupBG->SetVisible( false );
  398. if( m_pSuddenDeathLabel )
  399. m_pSuddenDeathLabel->SetVisible( false );
  400. if( m_pSuddenDeathBG )
  401. m_pSuddenDeathBG->SetVisible( false );
  402. if( m_pServerTimeLabel )
  403. m_pServerTimeLabel->SetVisible( false );
  404. if ( m_pServerTimeLabelBG )
  405. m_pServerTimeLabelBG->SetVisible( false );
  406. return;
  407. }
  408. bool bInSetup = TFGameRules()->InSetup();
  409. bool bInWaitingForPlayers = TFGameRules()->IsInWaitingForPlayers();
  410. if ( m_pSetupBG && m_pSetupLabel )
  411. {
  412. // get the time remaining (in seconds)
  413. if ( pTimer )
  414. {
  415. m_pSetupBG->SetVisible( bInSetup );
  416. m_pSetupLabel->SetVisible( bInSetup );
  417. }
  418. }
  419. // Set the Sudden Death panels to be visible
  420. if ( m_pSuddenDeathBG && m_pSuddenDeathLabel )
  421. {
  422. bool bInSD = TFGameRules()->InStalemate() == true && TFGameRules()->IsInArenaMode() == false;
  423. if ( bInSD != m_pSuddenDeathLabel->IsVisible() )
  424. {
  425. if ( bInSD )
  426. {
  427. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "SuddenDeathLabelPulseRed" );
  428. }
  429. else
  430. {
  431. g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( this, "SuddenDeathLabelPulseRed" );
  432. }
  433. }
  434. m_pSuddenDeathBG->SetVisible( bInSD );
  435. m_pSuddenDeathLabel->SetVisible( bInSD );
  436. }
  437. if ( m_pOvertimeBG && m_pOvertimeLabel )
  438. {
  439. bool bInOver = TFGameRules()->InOvertime();
  440. if ( TFGameRules()->IsInKothMode() )
  441. {
  442. if ( pTimer )
  443. {
  444. if ( bInOver )
  445. {
  446. if ( pTimer->GetTimeRemaining() <= 0 )
  447. {
  448. bInOver = true;
  449. m_bCachedOvertime = true;
  450. }
  451. else
  452. {
  453. bInOver = false;
  454. }
  455. }
  456. else
  457. {
  458. if ( m_bCachedOvertime )
  459. {
  460. if ( pTimer->GetTimeRemaining() <= 0 )
  461. {
  462. bInOver = true;
  463. }
  464. else
  465. {
  466. m_bCachedOvertime = false;
  467. }
  468. }
  469. }
  470. }
  471. }
  472. if ( bInOver )
  473. {
  474. // need to turn off the SuddenDeath images if they're on
  475. if ( m_pSuddenDeathBG && m_pSuddenDeathLabel )
  476. {
  477. m_pSuddenDeathBG->SetVisible( false );
  478. m_pSuddenDeathLabel->SetVisible( false );
  479. }
  480. if ( !m_pOvertimeLabel->IsVisible() )
  481. {
  482. m_pOvertimeBG->SetVisible( true );
  483. m_pOvertimeLabel->SetVisible( true );
  484. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "OvertimeLabelPulseRed" );
  485. CheckClockLabelLength( m_pOvertimeLabel, m_pOvertimeBG );
  486. }
  487. }
  488. else
  489. {
  490. m_pOvertimeBG->SetVisible( false );
  491. m_pOvertimeLabel->SetVisible( false );
  492. g_pClientMode->GetViewportAnimationController()->StopAnimationSequence( this, "OvertimeLabelPulseRed" );
  493. }
  494. }
  495. if ( m_pWaitingForPlayersBG && m_pWaitingForPlayersLabel )
  496. {
  497. m_pWaitingForPlayersBG->SetVisible( bInWaitingForPlayers );
  498. m_pWaitingForPlayersLabel->SetVisible( bInWaitingForPlayers );
  499. if ( bInWaitingForPlayers )
  500. {
  501. // can't be waiting for players *AND* in setup at the same time
  502. if ( m_pSetupBG && m_pSetupLabel )
  503. {
  504. m_pSetupBG->SetVisible( false );
  505. m_pSetupLabel->SetVisible( false );
  506. }
  507. CheckClockLabelLength( m_pWaitingForPlayersLabel, m_pWaitingForPlayersBG );
  508. }
  509. }
  510. if ( m_pServerTimeLabel && m_pServerTimeLabelBG )
  511. {
  512. // This appears in the same space after SetUp and WaitingForPlayers is gone
  513. bool bDisplayServerTimerEnabled = tf_hud_show_servertimelimit.GetInt() && !bInSetup && !bInWaitingForPlayers;
  514. if ( m_pServerTimeLabel->IsVisible() != bDisplayServerTimerEnabled )
  515. m_pServerTimeLabel->SetVisible( bDisplayServerTimerEnabled );
  516. if ( m_pServerTimeLabelBG->IsVisible() != bDisplayServerTimerEnabled )
  517. m_pServerTimeLabelBG->SetVisible( bDisplayServerTimerEnabled );
  518. }
  519. }
  520. //-----------------------------------------------------------------------------
  521. // Purpose:
  522. //-----------------------------------------------------------------------------
  523. void CTFHudTimeStatus::Reset()
  524. {
  525. m_flNextThink = gpGlobals->curtime + 0.05f;
  526. m_iTimerIndex = 0;
  527. m_iTimerDeltaHead = 0;
  528. for( int i = 0 ; i < NUM_TIMER_DELTA_ITEMS ; i++ )
  529. {
  530. m_TimerDeltaItems[i].m_flDieTime = 0.0f;
  531. }
  532. }
  533. //-----------------------------------------------------------------------------
  534. // Purpose:
  535. //-----------------------------------------------------------------------------
  536. void CTFHudTimeStatus::ApplySchemeSettings( IScheme *pScheme )
  537. {
  538. KeyValues *pConditions = NULL;
  539. if ( TFGameRules() )
  540. {
  541. const IMatchGroupDescription* pMatch = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  542. if ( pMatch && pMatch->m_params.m_bUseMatchHud )
  543. {
  544. pConditions = new KeyValues( "conditions" );
  545. AddSubKeyNamed( pConditions, "if_match" );
  546. }
  547. }
  548. // load control settings...
  549. LoadControlSettings( "resource/UI/HudObjectiveTimePanel.res", NULL, NULL, pConditions );
  550. if ( pConditions )
  551. {
  552. pConditions->deleteThis();
  553. }
  554. m_pProgressBar = dynamic_cast<CTFProgressBar *>( FindChildByName( "TimePanelProgressBar" ) );
  555. m_pOvertimeLabel = dynamic_cast<CExLabel *>( FindChildByName( "OvertimeLabel" ) );
  556. m_pOvertimeBG = FindChildByName( "OvertimeBG" );
  557. m_pSuddenDeathLabel = dynamic_cast<CExLabel *>( FindChildByName( "SuddenDeathLabel" ) );
  558. m_pSuddenDeathBG = FindChildByName( "SuddenDeathBG" );
  559. m_pWaitingForPlayersLabel = dynamic_cast<CExLabel *>( FindChildByName( "WaitingForPlayersLabel" ) );
  560. m_pWaitingForPlayersBG = FindChildByName("WaitingForPlayersBG" );
  561. m_pSetupLabel = dynamic_cast<CExLabel *>( FindChildByName( "SetupLabel" ) );
  562. m_pSetupBG = FindChildByName("SetupBG" );
  563. m_pTimerBG = dynamic_cast<ScalableImagePanel *>( FindChildByName( "TimePanelBG" ) );
  564. SetTeamBackground();
  565. m_pServerTimeLabel = dynamic_cast< CExLabel* >( FindChildByName( "ServerTimeLimitLabel" ) );
  566. m_pServerTimeLabelBG = FindChildByName("ServerTimeLimitLabelBG" );
  567. m_flNextThink = 0.0f;
  568. m_iTimerIndex = 0;
  569. SetExtraTimePanels();
  570. BaseClass::ApplySchemeSettings( pScheme );
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose:
  574. //-----------------------------------------------------------------------------
  575. void CTFHudTimeStatus::OnThink()
  576. {
  577. if ( m_flNextThink < gpGlobals->curtime )
  578. {
  579. CTeamRoundTimer *pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( m_iTimerIndex ) );
  580. if ( TFGameRules() )
  581. {
  582. if ( m_bKothMode != TFGameRules()->IsInKothMode() )
  583. {
  584. m_bKothMode = TFGameRules()->IsInKothMode();
  585. InvalidateLayout( false, true );
  586. }
  587. }
  588. // get the time remaining (in seconds)
  589. if ( pTimer )
  590. {
  591. int nTotalTime = pTimer->GetTimerMaxLength();
  592. int nTimeRemaining = pTimer->GetTimeRemaining();
  593. int nTimeToDisplay = nTimeRemaining;
  594. if ( !pTimer->ShowTimeRemaining() )
  595. {
  596. nTimeToDisplay = nTotalTime - nTimeToDisplay;
  597. }
  598. if ( m_pTimeValue && m_pTimeValue->IsVisible() )
  599. {
  600. // set our label
  601. int nMinutes = 0;
  602. int nSeconds = 0;
  603. char temp[256];
  604. if ( nTimeToDisplay <= 0 )
  605. {
  606. nMinutes = 0;
  607. nSeconds = 0;
  608. if ( TFGameRules() && TFGameRules()->IsInKothMode() )
  609. {
  610. SetExtraTimePanels();
  611. }
  612. }
  613. else
  614. {
  615. nMinutes = nTimeToDisplay / 60;
  616. nSeconds = nTimeToDisplay % 60;
  617. }
  618. Q_snprintf( temp, sizeof( temp ), "%d:%02d", nMinutes, nSeconds );
  619. m_pTimeValue->SetText( temp );
  620. }
  621. // let the progress bar know the percentage of time that's passed ( 0.0 -> 1.0 )
  622. if ( m_pProgressBar && m_pProgressBar->IsVisible() )
  623. {
  624. if ( nTotalTime == 0 )
  625. {
  626. m_pProgressBar->SetPercentage( 0 );
  627. }
  628. else
  629. {
  630. m_pProgressBar->SetPercentage( ( (float)nTotalTime - nTimeRemaining ) / (float)nTotalTime );
  631. }
  632. }
  633. // Optional display of mp_timelimit on HUD
  634. if ( m_pServerTimeLabel && m_pServerTimeLabelBG )
  635. {
  636. int nServerTimeLimit = mp_timelimit.GetInt() * 60;
  637. bool bDisplayServerTimerEnabled = tf_hud_show_servertimelimit.GetInt() &&
  638. TFGameRules() &&
  639. !TFGameRules()->InSetup() &&
  640. !TFGameRules()->IsInWaitingForPlayers() &&
  641. pTimer->IsRoundMaxTimerSet() &&
  642. nServerTimeLimit;
  643. if ( m_pServerTimeLabel->IsVisible() != bDisplayServerTimerEnabled )
  644. {
  645. m_pServerTimeLabel->SetVisible( bDisplayServerTimerEnabled );
  646. }
  647. if ( m_pServerTimeLabelBG->IsVisible() != bDisplayServerTimerEnabled )
  648. {
  649. m_pServerTimeLabelBG->SetVisible( bDisplayServerTimerEnabled );
  650. }
  651. // Only display server timelimit when the round timer isn't showing mp_timelimit (e.g. ctf_2fort)
  652. if ( bDisplayServerTimerEnabled )
  653. {
  654. wchar_t wzServerTimeHrsLeft[128];
  655. wchar_t wzServerTimeMinLeft[128];
  656. wchar_t wzServerTimeSecLeft[128];
  657. wchar_t wzServerTimeLeft[128];
  658. int iTimeLeft = 0;
  659. int iHours = 0;
  660. int iMinutes = 0;
  661. int iSeconds = 0;
  662. if ( !nServerTimeLimit )
  663. {
  664. g_pVGuiLocalize->ConstructString_safe( wzServerTimeLeft, g_pVGuiLocalize->Find( "#TF_HUD_ServerNoTimeLimit" ), 0);
  665. SetDialogVariable( "servertimeleft", wzServerTimeLeft );
  666. return;
  667. }
  668. iTimeLeft = ( TFGameRules() && TFGameRules()->GetTimeLeft() > 0 ) ? TFGameRules()->GetTimeLeft() : 0;
  669. if ( iTimeLeft == 0 )
  670. {
  671. g_pVGuiLocalize->ConstructString_safe( wzServerTimeLeft, g_pVGuiLocalize->Find( "#TF_HUD_ServerChangeOnRoundEnd" ), 0);
  672. SetDialogVariable( "servertimeleft", wzServerTimeLeft );
  673. return;
  674. }
  675. iHours = iTimeLeft / 3600;
  676. iMinutes = (iTimeLeft % 3600) / 60;
  677. iSeconds = (iTimeLeft % 60);
  678. _snwprintf( wzServerTimeHrsLeft, ARRAYSIZE( wzServerTimeHrsLeft ), L"%i", iHours );
  679. _snwprintf( wzServerTimeMinLeft, ARRAYSIZE( wzServerTimeMinLeft ), L"%02i", iMinutes );
  680. _snwprintf( wzServerTimeSecLeft, ARRAYSIZE( wzServerTimeSecLeft ), L"%02i", iSeconds );
  681. if (iHours == 0)
  682. {
  683. g_pVGuiLocalize->ConstructString_safe( wzServerTimeLeft, g_pVGuiLocalize->Find( "#TF_HUD_ServerTimeLeftNoHours" ), 2, wzServerTimeMinLeft, wzServerTimeSecLeft);
  684. SetDialogVariable( "servertimeleft", wzServerTimeLeft );
  685. return;
  686. }
  687. g_pVGuiLocalize->ConstructString_safe( wzServerTimeLeft, g_pVGuiLocalize->Find( "#TF_HUD_ServerTimeLeft" ), 3, wzServerTimeHrsLeft, wzServerTimeMinLeft, wzServerTimeSecLeft);
  688. SetDialogVariable( "servertimeleft", wzServerTimeLeft );
  689. }
  690. }
  691. }
  692. m_flNextThink = gpGlobals->curtime + 0.1f;
  693. }
  694. if ( TFGameRules() && TFGameRules()->IsInArenaMode() == true && tf_arena_round_time.GetInt() > 0 )
  695. {
  696. CHudArenaPlayerCount *pPlayerCount = ( CHudArenaPlayerCount * )GET_HUDELEMENT( CHudArenaPlayerCount );
  697. int iSelfX, iSelfY;
  698. GetPos( iSelfX, iSelfY );
  699. if ( pPlayerCount && pPlayerCount->IsVisible() )
  700. {
  701. int iX, iY, iTall, iWide;
  702. pPlayerCount->GetBounds( iX, iY, iWide, iTall );
  703. SetPos( iSelfX, iY + iTall - YRES( 12 ) );
  704. }
  705. else
  706. {
  707. SetPos( iSelfX, 0 );
  708. }
  709. }
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose: Paint the deltas
  713. //-----------------------------------------------------------------------------
  714. void CTFHudTimeStatus::Paint( void )
  715. {
  716. BaseClass::Paint();
  717. for ( int i = 0 ; i < NUM_TIMER_DELTA_ITEMS ; i++ )
  718. {
  719. // update all the valid delta items
  720. if ( m_TimerDeltaItems[i].m_flDieTime > gpGlobals->curtime )
  721. {
  722. // position and alpha are determined from the lifetime
  723. // color is determined by the delta - green for positive, red for negative
  724. Color c = ( m_TimerDeltaItems[i].m_nAmount > 0 ) ? m_DeltaPositiveColor : m_DeltaNegativeColor;
  725. float flLifetimePercent = ( m_TimerDeltaItems[i].m_flDieTime - gpGlobals->curtime ) / m_flDeltaLifetime;
  726. // fade out after half our lifetime
  727. if ( flLifetimePercent < 0.5 )
  728. {
  729. c[3] = (int)( 255.0f * ( flLifetimePercent / 0.5 ) );
  730. }
  731. float flHeight = ( m_flDeltaItemStartPos - m_flDeltaItemEndPos );
  732. float flYPos = m_flDeltaItemEndPos + flLifetimePercent * flHeight;
  733. surface()->DrawSetTextFont( m_hDeltaItemFont );
  734. surface()->DrawSetTextColor( c );
  735. surface()->DrawSetTextPos( m_flDeltaItemX, (int)flYPos );
  736. wchar_t wBuf[20];
  737. int nMinutes, nSeconds;
  738. int nClockTime = ( m_TimerDeltaItems[i].m_nAmount > 0 ) ? m_TimerDeltaItems[i].m_nAmount : ( m_TimerDeltaItems[i].m_nAmount * -1 );
  739. nMinutes = nClockTime / 60;
  740. nSeconds = nClockTime % 60;
  741. if ( m_TimerDeltaItems[i].m_nAmount > 0 )
  742. {
  743. V_swprintf_safe( wBuf, L"+%d:%02d", nMinutes, nSeconds );
  744. }
  745. else
  746. {
  747. V_swprintf_safe( wBuf, L"-%d:%02d", nMinutes, nSeconds );
  748. }
  749. surface()->DrawPrintText( wBuf, wcslen(wBuf), FONT_DRAW_NONADDITIVE );
  750. }
  751. }
  752. }
  753. DECLARE_HUDELEMENT( CTFHudKothTimeStatus );
  754. //-----------------------------------------------------------------------------
  755. // Purpose:
  756. //-----------------------------------------------------------------------------
  757. CTFHudKothTimeStatus::CTFHudKothTimeStatus( const char *pElementName )
  758. : CHudElement( pElementName )
  759. , BaseClass( NULL, "HudKothTimeStatus" )
  760. , m_pBluePanel( NULL )
  761. , m_pRedPanel( NULL )
  762. , m_pActiveTimerBG( NULL )
  763. , m_nActiveTeam( TEAM_UNASSIGNED )
  764. {
  765. Panel *pParent = g_pClientMode->GetViewport();
  766. SetParent( pParent );
  767. m_pBluePanel = new CTFHudTimeStatus( this, "BlueTimer" );
  768. m_pRedPanel = new CTFHudTimeStatus( this, "RedTimer" );
  769. if ( m_pRedPanel )
  770. {
  771. m_pRedPanel->SetTeam( TF_TEAM_RED );
  772. }
  773. SetHiddenBits( 0 );
  774. RegisterForRenderGroup( "mid" );
  775. RegisterForRenderGroup( "commentary" );
  776. }
  777. //-----------------------------------------------------------------------------
  778. // Purpose:
  779. //-----------------------------------------------------------------------------
  780. CTFHudKothTimeStatus::~CTFHudKothTimeStatus()
  781. {
  782. }
  783. //-----------------------------------------------------------------------------
  784. // Purpose:
  785. //-----------------------------------------------------------------------------
  786. bool CTFHudKothTimeStatus::ShouldDraw()
  787. {
  788. if ( TFGameRules() )
  789. {
  790. if ( TFGameRules()->ShowMatchSummary() )
  791. return false;
  792. if ( TFGameRules()->IsInKothMode() && !TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
  793. {
  794. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  795. if ( pPlayer )
  796. {
  797. if ( pPlayer->GetObserverMode() != OBS_MODE_FREEZECAM )
  798. {
  799. return CHudElement::ShouldDraw();
  800. }
  801. }
  802. }
  803. }
  804. return false;
  805. }
  806. //-----------------------------------------------------------------------------
  807. // Purpose:
  808. //-----------------------------------------------------------------------------
  809. void CTFHudKothTimeStatus::SetVisible( bool bVisible )
  810. {
  811. BaseClass::SetVisible( bVisible );
  812. if ( ShouldUseMatchHUD() )
  813. {
  814. UpdateActiveTeam();
  815. }
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Purpose:
  819. //-----------------------------------------------------------------------------
  820. void CTFHudKothTimeStatus::Reset()
  821. {
  822. if ( m_pBluePanel )
  823. {
  824. m_pBluePanel->Reset();
  825. }
  826. if ( m_pRedPanel )
  827. {
  828. m_pRedPanel->Reset();
  829. }
  830. }
  831. //-----------------------------------------------------------------------------
  832. // Purpose:
  833. //-----------------------------------------------------------------------------
  834. void CTFHudKothTimeStatus::ApplySchemeSettings( IScheme *pScheme )
  835. {
  836. BaseClass::ApplySchemeSettings( pScheme );
  837. KeyValues *pConditions = NULL;
  838. if ( TFGameRules() )
  839. {
  840. const IMatchGroupDescription* pMatch = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  841. if ( pMatch && pMatch->m_params.m_bUseMatchHud )
  842. {
  843. pConditions = new KeyValues( "conditions" );
  844. AddSubKeyNamed( pConditions, "if_match" );
  845. }
  846. }
  847. LoadControlSettings( "resource/UI/HudObjectiveKothTimePanel.res", NULL, NULL, pConditions );
  848. m_pActiveTimerBG = FindChildByName( "ActiveTimerBG" );
  849. UpdateActiveTeam();
  850. }
  851. //-----------------------------------------------------------------------------
  852. // Purpose:
  853. //-----------------------------------------------------------------------------
  854. void CTFHudKothTimeStatus::UpdateActiveTeam( void )
  855. {
  856. if ( ShouldUseMatchHUD() )
  857. {
  858. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pRedPanel, m_nActiveTeam == TF_TEAM_RED ? "ActiveTimerHighlight" : "ActiveTimerDim", false );
  859. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_pBluePanel, m_nActiveTeam == TF_TEAM_BLUE ? "ActiveTimerHighlight" : "ActiveTimerDim", false );
  860. }
  861. else if ( m_pActiveTimerBG )
  862. {
  863. if ( m_nActiveTeam != TEAM_UNASSIGNED )
  864. {
  865. if ( !m_pActiveTimerBG->IsVisible() )
  866. {
  867. m_pActiveTimerBG->SetVisible( true );
  868. }
  869. m_pActiveTimerBG->SetAlpha( 255 );
  870. int xNewPos = m_nBlueActiveXPos;
  871. if ( m_nActiveTeam == TF_TEAM_RED )
  872. {
  873. xNewPos = m_nRedActiveXPos;
  874. }
  875. int xPos, yPos;
  876. m_pActiveTimerBG->GetPos( xPos, yPos );
  877. m_pActiveTimerBG->SetPos( xNewPos, yPos );
  878. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "ActiveTimerBGPulse" );
  879. }
  880. else
  881. {
  882. if ( m_pActiveTimerBG->IsVisible() )
  883. {
  884. m_pActiveTimerBG->SetVisible( false );
  885. }
  886. }
  887. }
  888. }
  889. //-----------------------------------------------------------------------------
  890. // Purpose:
  891. //-----------------------------------------------------------------------------
  892. void CTFHudKothTimeStatus::Think( void )
  893. {
  894. if ( !TFGameRules() || !IsVisible() )
  895. return;
  896. int nActiveTeam = TEAM_UNASSIGNED;
  897. CTeamRoundTimer *pTimer = TFGameRules()->GetBlueKothRoundTimer();
  898. if ( pTimer )
  899. {
  900. if ( m_pBluePanel )
  901. {
  902. if ( pTimer->entindex() != m_pBluePanel->GetTimerIndex() )
  903. {
  904. m_pBluePanel->SetTimerIndex( pTimer->entindex() );
  905. }
  906. }
  907. if ( !pTimer->IsTimerPaused() )
  908. {
  909. nActiveTeam = TF_TEAM_BLUE;
  910. }
  911. }
  912. pTimer = TFGameRules()->GetRedKothRoundTimer();
  913. if ( pTimer )
  914. {
  915. if ( m_pRedPanel )
  916. {
  917. if ( pTimer->entindex() != m_pRedPanel->GetTimerIndex() )
  918. {
  919. m_pRedPanel->SetTimerIndex( pTimer->entindex() );
  920. }
  921. }
  922. if ( !pTimer->IsTimerPaused() )
  923. {
  924. nActiveTeam = TF_TEAM_RED;
  925. }
  926. }
  927. if ( nActiveTeam != m_nActiveTeam )
  928. {
  929. m_nActiveTeam = nActiveTeam;
  930. UpdateActiveTeam();
  931. }
  932. }