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.

907 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "hudelement.h"
  8. #include "iclientmode.h"
  9. #include <vgui_controls/AnimationController.h>
  10. #include <vgui_controls/EditablePanel.h>
  11. #include <vgui_controls/SectionedListPanel.h>
  12. #include <vgui_controls/ImageList.h>
  13. #include "vgui_avatarimage.h"
  14. #include "tf_hud_match_status.h"
  15. #include "tf_gamerules.h"
  16. #include "c_tf_team.h"
  17. #include "vgui_controls/ScalableImagePanel.h"
  18. #include "tf_time_panel.h"
  19. #include "c_team_objectiveresource.h"
  20. #include "game_controls/spectatorgui.h"
  21. #include "c_tf_playerresource.h"
  22. #include "tf_gc_client.h"
  23. #include "tf_match_description.h"
  24. #include "tf_hud_tournament.h"
  25. #include "tf_classmenu.h"
  26. extern ConVar mp_winlimit;
  27. extern ConVar mp_tournament_stopwatch;
  28. using namespace vgui;
  29. void AddSubKeyNamed( KeyValues *pKeys, const char *pszName );
  30. //-----------------------------------------------------------------------------
  31. // Purpose: Use the new match HUD or the old? Right now, Comp is the key
  32. //-----------------------------------------------------------------------------
  33. bool ShouldUseMatchHUD()
  34. {
  35. const IMatchGroupDescription* pMatchDesc = NULL;
  36. if ( GTFGCClientSystem()->BHaveLiveMatch() )
  37. {
  38. pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
  39. }
  40. else if ( TFGameRules() )
  41. {
  42. pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  43. }
  44. if ( pMatchDesc )
  45. {
  46. return pMatchDesc->m_params.m_bUseMatchHud;
  47. }
  48. return false;
  49. }
  50. const int g_nMaxSupportedRounds = 5;
  51. //-----------------------------------------------------------------------------
  52. // Purpose:
  53. //-----------------------------------------------------------------------------
  54. CRoundCounterPanel::CRoundCounterPanel( Panel *parent, const char *panelName )
  55. : BaseClass( parent, panelName )
  56. , m_pRoundIndicatorKVs( NULL )
  57. , m_pRoundWinIndicatorRedKV( NULL )
  58. , m_pRoundWinIndicatorBlueKV( NULL )
  59. , m_bCountDirty( false )
  60. {
  61. ListenForGameEvent( "winlimit_changed" );
  62. ListenForGameEvent( "winpanel_show_scores" );
  63. ListenForGameEvent( "stop_watch_changed" );
  64. ListenForGameEvent( "teamplay_round_start" );
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose:
  68. //-----------------------------------------------------------------------------
  69. CRoundCounterPanel::~CRoundCounterPanel()
  70. {
  71. if ( m_pRoundIndicatorKVs )
  72. m_pRoundIndicatorKVs->deleteThis();
  73. if ( m_pRoundWinIndicatorRedKV )
  74. m_pRoundWinIndicatorRedKV->deleteThis();
  75. if ( m_pRoundWinIndicatorBlueKV )
  76. m_pRoundWinIndicatorBlueKV->deleteThis();
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. //-----------------------------------------------------------------------------
  81. void CRoundCounterPanel::ApplySchemeSettings(IScheme *pScheme)
  82. {
  83. BaseClass::ApplySchemeSettings( pScheme );
  84. LoadControlSettings( "resource/UI/HudRoundCounter.res" );
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Put a copy of the specified keys in block pszKeyName from pKVIn
  88. // into pKV
  89. //-----------------------------------------------------------------------------
  90. void LoadKeyValues( KeyValues** pKV, KeyValues* pKVIn, const char* pszKeyName )
  91. {
  92. if ( (*pKV) )
  93. (*pKV)->deleteThis();
  94. (*pKV) = pKVIn->FindKey( pszKeyName );
  95. if ((*pKV))
  96. {
  97. (*pKV) = (*pKV)->MakeCopy();
  98. }
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose:
  102. //-----------------------------------------------------------------------------
  103. void CRoundCounterPanel::ApplySettings( KeyValues *inResourceData )
  104. {
  105. BaseClass::ApplySettings( inResourceData );
  106. LoadKeyValues( &m_pRoundIndicatorKVs, inResourceData, "RoundIndicatorPanel_kv" );
  107. LoadKeyValues( &m_pRoundWinIndicatorRedKV, inResourceData, "RoundWinPanelRed_kv" );
  108. LoadKeyValues( &m_pRoundWinIndicatorBlueKV, inResourceData, "RoundWinPanelBlue_kv" );
  109. CreateRoundPanels( m_vecBlueRoundIndicators, "RoundIndicator", m_pRoundIndicatorKVs );
  110. CreateRoundPanels( m_vecRedRoundIndicators, "RoundIndicator", m_pRoundIndicatorKVs );
  111. CreateRoundPanels( m_vecBlueWinIndicators, "WinIndicatorBlue", m_pRoundWinIndicatorBlueKV );
  112. CreateRoundPanels( m_vecRedWinIndicators, "WinIndicatorRed", m_pRoundWinIndicatorRedKV );
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose: Ensure there are the correct number of image panels. If not, create
  116. // them and apply the passed-in settings
  117. //-----------------------------------------------------------------------------
  118. void CRoundCounterPanel::CreateRoundPanels( ImageVector& vecImages, const char* pszName, KeyValues* pKVSettings )
  119. {
  120. int nMaxRounds = g_nMaxSupportedRounds;
  121. if ( vecImages.Count() != nMaxRounds )
  122. {
  123. FOR_EACH_VEC( vecImages, i )
  124. {
  125. vecImages[ i ]->MarkForDeletion();
  126. }
  127. vecImages.Purge();
  128. if ( nMaxRounds > 0 )
  129. {
  130. while ( nMaxRounds-- )
  131. {
  132. vecImages.AddToTail(new ImagePanel(this, pszName));
  133. }
  134. }
  135. }
  136. if ( pKVSettings )
  137. {
  138. FOR_EACH_VEC(vecImages, i)
  139. {
  140. vecImages[i]->ApplySettings( pKVSettings );
  141. }
  142. }
  143. }
  144. extern ConVar tf_attack_defend_map;
  145. //-----------------------------------------------------------------------------
  146. // Purpose: Loop through and conditionally set visible some panels
  147. //-----------------------------------------------------------------------------
  148. void VisibleCondition( CRoundCounterPanel::ImageVector& vecImages, int iMax )
  149. {
  150. bool bInStopWatch = tf_attack_defend_map.GetBool();
  151. FOR_EACH_VEC( vecImages, i )
  152. {
  153. vecImages[i]->SetVisible( i < iMax && !bInStopWatch );
  154. }
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose: Position all of the round panels and resize the background blue/red
  158. //-----------------------------------------------------------------------------
  159. void CRoundCounterPanel::PerformLayout()
  160. {
  161. BaseClass::PerformLayout();
  162. if ( !TFGameRules() || !ShouldUseMatchHUD() )
  163. return;
  164. C_TFTeam* pTeams[ TF_TEAM_COUNT ];
  165. pTeams[ TF_TEAM_RED ] = GetGlobalTFTeam( TF_TEAM_RED );
  166. pTeams[ TF_TEAM_BLUE ] = GetGlobalTFTeam( TF_TEAM_BLUE );
  167. if ( !pTeams[ TF_TEAM_RED ] || !pTeams[ TF_TEAM_BLUE ] )
  168. return;
  169. // Layout the round indicators
  170. LayoutPanels( m_vecBlueRoundIndicators, EAlignment::ALIGN_WEST, (GetWide() / 2) - m_nIndicatorStartOffset, m_nIndicatorPanelStep );
  171. VisibleCondition( m_vecBlueRoundIndicators, mp_winlimit.GetInt() );
  172. LayoutPanels( m_vecRedRoundIndicators, EAlignment::ALIGN_EAST, (GetWide() / 2) + m_nIndicatorStartOffset, m_nIndicatorPanelStep );
  173. VisibleCondition( m_vecRedRoundIndicators, mp_winlimit.GetInt() );
  174. // Layout the win indicators
  175. LayoutPanels( m_vecBlueWinIndicators, EAlignment::ALIGN_WEST, (GetWide() / 2) - m_nIndicatorStartOffset, m_nIndicatorPanelStep );
  176. VisibleCondition( m_vecBlueWinIndicators, Min( mp_winlimit.GetInt(), pTeams[ TF_TEAM_BLUE ]->m_iScore ) );
  177. LayoutPanels( m_vecRedWinIndicators, EAlignment::ALIGN_EAST, (GetWide() / 2) + m_nIndicatorStartOffset, m_nIndicatorPanelStep );
  178. VisibleCondition( m_vecRedWinIndicators, Min( mp_winlimit.GetInt(), pTeams[ TF_TEAM_RED ]->m_iScore ) );
  179. }
  180. void CRoundCounterPanel::OnThink()
  181. {
  182. if ( m_bCountDirty )
  183. {
  184. int nNumVisible = 0;
  185. FOR_EACH_VEC( m_vecBlueRoundIndicators, i )
  186. {
  187. if ( m_vecBlueRoundIndicators[i]->IsVisible() )
  188. ++nNumVisible;
  189. }
  190. if ( nNumVisible != mp_winlimit.GetInt() )
  191. {
  192. InvalidateLayout();
  193. m_bCountDirty = false;
  194. }
  195. }
  196. }
  197. void CRoundCounterPanel::FireGameEvent(IGameEvent * event )
  198. {
  199. if ( FStrEq( event->GetName(), "winlimit_changed" ) ) // Resize if the win limit changes
  200. {
  201. m_bCountDirty = true;
  202. }
  203. else if ( FStrEq( event->GetName(), "winpanel_show_scores" ) // Conditionally hide the win markers
  204. || FStrEq( event->GetName(), "stop_watch_changed" ) // Match the timing of the win panel "Ding!" when the scores update
  205. || FStrEq( event->GetName(), "teamplay_round_start" ) ) // Make sure we're accurate when the round starts in case the hud event didnt happen
  206. {
  207. InvalidateLayout( true );
  208. }
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Purpose: Layout the round panels
  212. //-----------------------------------------------------------------------------
  213. void CRoundCounterPanel::LayoutPanels( ImageVector& vecImages, EAlignment eAlignment, int nStartPos, int nMaxWide )
  214. {
  215. if ( !mp_winlimit.GetInt() )
  216. return;
  217. FOR_EACH_VEC( vecImages, i )
  218. {
  219. Panel* pPanel = vecImages[ i ];
  220. const int nXStartPos = eAlignment == ALIGN_EAST ? nStartPos : nStartPos;
  221. const int nStep = ( nMaxWide / mp_winlimit.GetInt() );
  222. const int nXOffset = nStep * i;
  223. // Step out the panels by the steph width
  224. int nXPos = eAlignment == ALIGN_EAST ? nXStartPos + nXOffset - ( pPanel->GetWide() / 2 ) + ( nStep / 2 )
  225. : nXStartPos - nXOffset - ( pPanel->GetWide() / 2 ) - ( nStep / 2 );
  226. pPanel->SetPos( nXPos, pPanel->GetYPos() );
  227. }
  228. }
  229. DECLARE_HUDELEMENT( CTFHudMatchStatus );
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. //-----------------------------------------------------------------------------
  233. CTFHudMatchStatus::CTFHudMatchStatus(const char *pElementName)
  234. : CHudElement(pElementName)
  235. , BaseClass(NULL, "HudMatchStatus")
  236. , m_pTimePanel( NULL )
  237. , m_bUseMatchHUD( false )
  238. , m_eMatchGroupSettings( k_nMatchGroup_Invalid )
  239. {
  240. Panel *pParent = g_pClientMode->GetViewport();
  241. SetParent(pParent);
  242. SetHiddenBits( HIDEHUD_MISCSTATUS );
  243. m_pMatchStartModelPanel = new CModelPanel( this, "MatchDoors" );
  244. m_pRoundCounter = new CRoundCounterPanel( this, "RoundCounter" );
  245. m_pTimePanel = new CTFHudTimeStatus( this, "ObjectiveStatusTimePanel" );
  246. m_pRoundSignModel = new CModelPanel( this, "RoundSignModel" );
  247. m_pTeamStatus = new CTFTeamStatus( this, "TeamStatus" );
  248. m_pBlueTeamPanel = new vgui::EditablePanel( this, "BlueTeamPanel" );
  249. m_pPlayerListBlue = new vgui::SectionedListPanel( m_pBlueTeamPanel, "BluePlayerList" );
  250. m_pBlueLeaderAvatarImage = new CAvatarImagePanel( m_pBlueTeamPanel, "BlueLeaderAvatar" );
  251. m_pBlueLeaderAvatarBG = new EditablePanel( m_pBlueTeamPanel, "BlueLeaderAvatarBG" );
  252. m_pBlueTeamImage = new ImagePanel( m_pBlueTeamPanel, "BlueTeamImage" );
  253. m_pBlueTeamName = new CExLabel( m_pBlueTeamPanel, "BlueTeamLabel", "" );
  254. m_pRedTeamPanel = new vgui::EditablePanel( this, "RedTeamPanel" );
  255. m_pPlayerListRed = new vgui::SectionedListPanel( m_pRedTeamPanel, "RedPlayerList" );
  256. m_pRedLeaderAvatarImage = new CAvatarImagePanel( m_pRedTeamPanel, "RedLeaderAvatar" );
  257. m_pRedLeaderAvatarBG = new EditablePanel( m_pRedTeamPanel, "RedLeaderAvatarBG" );
  258. m_pRedTeamImage = new ImagePanel( m_pRedTeamPanel, "RedTeamImage" );
  259. m_pRedTeamName = new CExLabel( m_pRedTeamPanel, "RedTeamLabel", "" );
  260. m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) );
  261. m_mapAvatarsToImageList.RemoveAll();
  262. ListenForGameEvent( "teamplay_round_start" );
  263. ListenForGameEvent( "restart_timer_time" );
  264. ListenForGameEvent( "show_match_summary" );
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose:
  268. //-----------------------------------------------------------------------------
  269. CTFHudMatchStatus::~CTFHudMatchStatus()
  270. {
  271. if ( NULL != m_pImageList )
  272. {
  273. delete m_pImageList;
  274. m_pImageList = NULL;
  275. }
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. void CTFHudMatchStatus::Reset()
  281. {
  282. SetPanelsVisible();
  283. if ( m_pTimePanel )
  284. {
  285. m_pTimePanel->Reset();
  286. }
  287. if ( m_pTeamStatus )
  288. {
  289. m_pTeamStatus->Reset();
  290. }
  291. CHudElement::Reset();
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. //-----------------------------------------------------------------------------
  296. void CTFHudMatchStatus::SetPanelsVisible()
  297. {
  298. m_pRoundCounter->SetVisible( ShouldUseMatchHUD() );
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose:
  302. //-----------------------------------------------------------------------------
  303. void CTFHudMatchStatus::ApplySchemeSettings(IScheme *pScheme)
  304. {
  305. BaseClass::ApplySchemeSettings(pScheme);
  306. KeyValues *pConditions = NULL;
  307. if ( ShouldUseMatchHUD() )
  308. {
  309. pConditions = new KeyValues( "conditions" );
  310. AddSubKeyNamed( pConditions, "if_match" );
  311. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( GTFGCClientSystem()->GetLiveMatchGroup() );
  312. if ( pMatchDesc )
  313. {
  314. if ( pMatchDesc->m_params.m_pmm_match_group_size->GetInt() > 12 )
  315. {
  316. AddSubKeyNamed( pConditions, "if_large" );
  317. }
  318. }
  319. }
  320. // load control settings...
  321. LoadControlSettings( "resource/UI/HudMatchStatus.res", NULL, NULL, pConditions );
  322. if ( pConditions )
  323. {
  324. pConditions->deleteThis();
  325. }
  326. if ( m_pImageList )
  327. delete m_pImageList;
  328. m_pImageList = new ImageList( false );
  329. m_mapAvatarsToImageList.RemoveAll();
  330. m_pPlayerListBlue->SetImageList( m_pImageList, false );
  331. m_pPlayerListRed->SetImageList( m_pImageList, false );
  332. InitPlayerList( m_pPlayerListBlue, TF_TEAM_BLUE );
  333. InitPlayerList( m_pPlayerListRed, TF_TEAM_RED );
  334. m_hPlayerListFont = pScheme->GetFont( "Default", true );
  335. UpdatePlayerList();
  336. UpdateTeamInfo();
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose:
  340. //-----------------------------------------------------------------------------
  341. void CTFHudMatchStatus::PerformLayout()
  342. {
  343. BaseClass::PerformLayout();
  344. SetPanelsVisible();
  345. }
  346. bool CTFHudMatchStatus::ShouldDraw( void )
  347. {
  348. // Force to draw during match summary so the doors show up. This panel
  349. // will try to hide itself if you're dead, but we want to ignore that
  350. // behavior and force us to draw.
  351. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  352. return true;
  353. if ( gViewPortInterface->GetActivePanel() )
  354. return false;
  355. return CHudElement::ShouldDraw();
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Purpose:
  359. //-----------------------------------------------------------------------------
  360. void CTFHudMatchStatus::OnThink()
  361. {
  362. if ( !TFGameRules() )
  363. return;
  364. bool bReload = false;
  365. bool bUseMatchHUD = ShouldUseMatchHUD();
  366. if ( bUseMatchHUD != m_bUseMatchHUD )
  367. {
  368. m_bUseMatchHUD = bUseMatchHUD;
  369. bReload = true;
  370. }
  371. EMatchGroup eCurrentGroup = TFGameRules()->GetCurrentMatchGroup();
  372. if ( eCurrentGroup != m_eMatchGroupSettings )
  373. {
  374. m_eMatchGroupSettings = eCurrentGroup;
  375. bReload = true;
  376. }
  377. if ( bReload )
  378. {
  379. InvalidateLayout( false, true );
  380. // The KOTH timers are their own hud element
  381. CTFHudKothTimeStatus *pKothHUD = GET_HUDELEMENT( CTFHudKothTimeStatus );
  382. if ( pKothHUD )
  383. {
  384. pKothHUD->InvalidateLayout( false, true );
  385. }
  386. }
  387. // check for an active timer and turn the time panel on or off if we need to
  388. if ( m_pTimePanel )
  389. {
  390. // Don't draw in freezecam, or when the game's not running
  391. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  392. bool bDisplayTimer = !( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM );
  393. if ( TeamplayRoundBasedRules()->IsInTournamentMode() && TeamplayRoundBasedRules()->IsInWaitingForPlayers() )
  394. {
  395. bDisplayTimer = false;
  396. }
  397. if ( bDisplayTimer )
  398. {
  399. // is the time panel still pointing at an active timer?
  400. int iCurrentTimer = m_pTimePanel->GetTimerIndex();
  401. CTeamRoundTimer *pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( iCurrentTimer ) );
  402. if ( pTimer && !pTimer->IsDormant() && !pTimer->IsDisabled() && pTimer->ShowInHud() )
  403. {
  404. // the current timer is fine, make sure the panel is visible
  405. bDisplayTimer = true;
  406. }
  407. else if ( ObjectiveResource() )
  408. {
  409. // check for a different timer
  410. int iActiveTimer = ObjectiveResource()->GetTimerToShowInHUD();
  411. pTimer = dynamic_cast< CTeamRoundTimer* >( ClientEntityList().GetEnt( iActiveTimer ) );
  412. bDisplayTimer = ( iActiveTimer != 0 && pTimer && !pTimer->IsDormant() );
  413. m_pTimePanel->SetTimerIndex( iActiveTimer );
  414. }
  415. }
  416. if ( bDisplayTimer && !TFGameRules()->ShowMatchSummary() )
  417. {
  418. if ( !TFGameRules()->IsInKothMode() )
  419. {
  420. if ( !m_pTimePanel->IsVisible() )
  421. {
  422. m_pTimePanel->SetVisible( true );
  423. // If our spectator GUI is visible, invalidate its layout so that it moves the reinforcement label
  424. if ( g_pSpectatorGUI )
  425. {
  426. g_pSpectatorGUI->InvalidateLayout();
  427. }
  428. }
  429. }
  430. else
  431. {
  432. bool bVisible = TeamplayRoundBasedRules()->IsInWaitingForPlayers();
  433. if ( m_pTimePanel->IsVisible() != bVisible )
  434. {
  435. m_pTimePanel->SetVisible( bVisible );
  436. // If our spectator GUI is visible, invalidate its layout so that it moves the reinforcement label
  437. if ( g_pSpectatorGUI )
  438. {
  439. g_pSpectatorGUI->InvalidateLayout();
  440. }
  441. }
  442. }
  443. }
  444. else
  445. {
  446. if ( m_pTimePanel->IsVisible() )
  447. {
  448. m_pTimePanel->SetVisible( false );
  449. }
  450. }
  451. }
  452. BaseClass::OnThink();
  453. }
  454. //-----------------------------------------------------------------------------
  455. // Purpose:
  456. //-----------------------------------------------------------------------------
  457. void CTFHudMatchStatus::FireGameEvent( IGameEvent * event )
  458. {
  459. if ( !ShouldUseMatchHUD() )
  460. return;
  461. if ( FStrEq("teamplay_round_start", event->GetName() ) )
  462. {
  463. // Drop the round sign right when the match starts on rounds > 1
  464. if ( TFGameRules()->GetRoundsPlayed() > 0 )
  465. {
  466. ShowRoundSign( TFGameRules()->GetRoundsPlayed() );
  467. }
  468. }
  469. else if ( FStrEq( "restart_timer_time", event->GetName() ) )
  470. {
  471. HandleCountdown( event->GetInt( "time" ) );
  472. }
  473. else if ( FStrEq( "show_match_summary", event->GetName() ) )
  474. {
  475. if ( m_pBlueTeamPanel )
  476. {
  477. m_pBlueTeamPanel->SetVisible( false );
  478. }
  479. if ( m_pRedTeamPanel )
  480. {
  481. m_pRedTeamPanel->SetVisible( false );
  482. }
  483. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  484. bool bForceDoors = false;
  485. #ifdef STAGING_ONLY
  486. bForceDoors = tf_test_match_summary.GetBool();
  487. #endif
  488. if ( bForceDoors || ( pMatchDesc && pMatchDesc->m_params.m_bShowPostRoundDoors ) )
  489. {
  490. if ( TFGameRules() && TFGameRules()->MapHasMatchSummaryStage() && ( bForceDoors || pMatchDesc->m_params.m_bUseMatchSummaryStage ) )
  491. {
  492. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchWinDoors", false );
  493. }
  494. else
  495. {
  496. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchWinDoors_NoOpen", false );
  497. }
  498. }
  499. }
  500. }
  501. void CTFHudMatchStatus::HandleCountdown( int nTime )
  502. {
  503. // Update the timer
  504. SetDialogVariable( "countdown", nTime );
  505. switch ( nTime )
  506. {
  507. case 2:
  508. // Drop the round sign with 2 seconds to go on the 1st round
  509. if ( TFGameRules()->GetRoundsPlayed() == 0 )
  510. {
  511. ShowRoundSign( TFGameRules()->GetRoundsPlayed() );
  512. }
  513. break;
  514. case 10:
  515. if ( TFGameRules()->GetRoundsPlayed() == 0 )
  516. {
  517. ShowMatchStartDoors();
  518. }
  519. else
  520. {
  521. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowCountdown", false );
  522. }
  523. break;
  524. }
  525. }
  526. #ifdef STAGING_ONLY
  527. ConVar tf_comp_door_skin_override( "tf_comp_door_skin_override", "-1", 0, "Skin override for the competitive doors. Set to -1 to not override calculated skin" );
  528. ConVar tf_comp_door_bodygroup_override( "tf_comp_door_bodygroup_override", "-1", 0, "Bodygroup override for the competitive doors. Set to -1 to not override calculated skin" );
  529. #endif
  530. //-----------------------------------------------------------------------------
  531. // Purpose:
  532. //-----------------------------------------------------------------------------
  533. void CTFHudMatchStatus::ShowMatchStartDoors()
  534. {
  535. if ( TFGameRules()->GetCurrentMatchGroup() == k_nMatchGroup_Invalid )
  536. return;
  537. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  538. int nSkin = 0;
  539. int nSubModel = 0;
  540. if ( pMatchDesc->BGetRoundDoorParameters( nSkin, nSubModel ) )
  541. {
  542. #ifdef STAGING_ONLY
  543. if ( tf_comp_door_skin_override.GetInt() != -1 )
  544. {
  545. nSkin = tf_comp_door_skin_override.GetInt();
  546. }
  547. if ( tf_comp_door_bodygroup_override.GetInt() != -1 )
  548. {
  549. nSubModel = tf_comp_door_bodygroup_override.GetInt();
  550. }
  551. #endif
  552. UpdatePlayerList();
  553. UpdateTeamInfo();
  554. if ( m_pMatchStartModelPanel->m_hModel == NULL )
  555. {
  556. m_pMatchStartModelPanel->UpdateModel();
  557. }
  558. m_pMatchStartModelPanel->SetBodyGroup( "logos", nSubModel );
  559. m_pMatchStartModelPanel->UpdateModel();
  560. m_pMatchStartModelPanel->SetSkin( nSkin );
  561. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "HudMatchStatus_ShowMatchStartDoors", false );
  562. // Hide the class selection panel. It sorts weird with the doors, and we dont have time to figure out why.
  563. gViewPortInterface->ShowPanel( PANEL_CLASS_RED, false );
  564. gViewPortInterface->ShowPanel( PANEL_CLASS_BLUE, false );
  565. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  566. if ( pLocalPlayer )
  567. {
  568. pLocalPlayer->EmitSound( pMatchDesc->m_params.m_pszMatchStartSound );
  569. }
  570. }
  571. }
  572. //-----------------------------------------------------------------------------
  573. // Purpose: Show the round sign with the specified round number
  574. //-----------------------------------------------------------------------------
  575. void CTFHudMatchStatus::ShowRoundSign( int nRoundNumber )
  576. {
  577. if ( TFGameRules()->GetCurrentMatchGroup() == k_nMatchGroup_Invalid )
  578. return;
  579. if ( !m_pRoundSignModel || !m_pRoundSignModel->m_pModelInfo )
  580. return;
  581. Assert( TFGameRules()->GetRoundsPlayed() >= 0 && TFGameRules()->GetRoundsPlayed() <= 6 );
  582. int nSkin = 0;
  583. int nBodyGroup = 0;
  584. if ( GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() )->BGetRoundStartBannerParameters( nSkin, nBodyGroup ) )
  585. {
  586. if ( m_pRoundSignModel->m_hModel == NULL )
  587. {
  588. m_pRoundSignModel->UpdateModel();
  589. }
  590. // Change the skin and bodygroup to be correct for the mode and round
  591. m_pRoundSignModel->SetBodyGroup( "logos", nBodyGroup );
  592. m_pRoundSignModel->m_pModelInfo->m_nSkin = nSkin;
  593. // Make the model actually update with the new look
  594. m_pRoundSignModel->SetPanelDirty();
  595. m_pRoundSignModel->UpdateModel();
  596. // Play the sign drop anim
  597. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence(this, "HudTournament_ShowRoundSign", false);
  598. }
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose: Used for sorting players
  602. //-----------------------------------------------------------------------------
  603. bool TFPlayerSortFunc( vgui::SectionedListPanel *list, int itemID1, int itemID2 )
  604. {
  605. KeyValues *it1 = list->GetItemData( itemID1 );
  606. KeyValues *it2 = list->GetItemData( itemID2 );
  607. Assert( it1 && it2 );
  608. // first compare score
  609. int v1 = it1->GetInt( "score" );
  610. int v2 = it2->GetInt( "score" );
  611. if ( v1 > v2 )
  612. return true;
  613. else if ( v1 < v2 )
  614. return false;
  615. // if score is the same, use player index to get deterministic sort
  616. int iPlayerIndex1 = it1->GetInt( "playerIndex" );
  617. int iPlayerIndex2 = it2->GetInt( "playerIndex" );
  618. return ( iPlayerIndex1 > iPlayerIndex2 );
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Purpose: Inits the player list in a list panel
  622. //-----------------------------------------------------------------------------
  623. void CTFHudMatchStatus::InitPlayerList( SectionedListPanel *pPlayerList, int nTeam )
  624. {
  625. pPlayerList->SetVerticalScrollbar( false );
  626. pPlayerList->RemoveAll();
  627. pPlayerList->RemoveAllSections();
  628. pPlayerList->AddSection( 0, "Players", TFPlayerSortFunc );
  629. pPlayerList->SetSectionAlwaysVisible( 0, true );
  630. pPlayerList->SetSectionDrawDividerBar( 0, false );
  631. pPlayerList->SetBorder( NULL );
  632. pPlayerList->SetMouseInputEnabled( false );
  633. pPlayerList->SetClickable( false );
  634. pPlayerList->AddColumnToSection( 0, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth );
  635. pPlayerList->AddColumnToSection( 0, "spacer", "", 0, m_iSpacerWidth );
  636. // the player avatar is always a fixed size, so as we change resolutions we need to vary the size of the name column to adjust the total width of all the columns
  637. int nExtraSpace = pPlayerList->GetWide() - m_iAvatarWidth - m_iSpacerWidth - m_iNameWidth - ( 2 * SectionedListPanel::COLUMN_DATA_INDENT ); // the SectionedListPanel will indent the columns on either end by SectionedListPanel::COLUMN_DATA_INDENT
  638. pPlayerList->AddColumnToSection( 0, "name", "", 0, m_iNameWidth + nExtraSpace );
  639. }
  640. //-----------------------------------------------------------------------------
  641. // Purpose: Updates the player list
  642. //-----------------------------------------------------------------------------
  643. void CTFHudMatchStatus::UpdatePlayerList()
  644. {
  645. m_pPlayerListRed->RemoveAll();
  646. m_pPlayerListRed->ClearAllColorOverrideForCell();
  647. m_pPlayerListBlue->RemoveAll();
  648. m_pPlayerListBlue->ClearAllColorOverrideForCell();
  649. if ( !g_TF_PR )
  650. return;
  651. for ( int playerIndex = 1; playerIndex <= MAX_PLAYERS; playerIndex++ )
  652. {
  653. if ( g_PR->IsConnected( playerIndex ) )
  654. {
  655. SectionedListPanel *pPlayerList = NULL;
  656. int nTeam = g_PR->GetTeam( playerIndex );
  657. switch ( nTeam )
  658. {
  659. case TF_TEAM_BLUE:
  660. pPlayerList = m_pPlayerListBlue;
  661. break;
  662. case TF_TEAM_RED:
  663. pPlayerList = m_pPlayerListRed;
  664. break;
  665. }
  666. if ( null == pPlayerList )
  667. continue;
  668. KeyValues *pKeyValues = new KeyValues( "data" );
  669. pKeyValues->SetInt( "playerIndex", playerIndex );
  670. pKeyValues->SetString( "name", g_TF_PR->GetPlayerName( playerIndex ) );
  671. UpdatePlayerAvatar( playerIndex, pKeyValues );
  672. int itemID = pPlayerList->AddItem( 0, pKeyValues );
  673. pPlayerList->SetItemFgColor( itemID, g_PR->GetTeamColor( nTeam ) );
  674. pPlayerList->SetItemBgColor( itemID, Color( 120, 120, 120, 80 ) );
  675. pPlayerList->SetItemBgHorizFillInset( itemID, m_iHorizFillInset );
  676. pPlayerList->SetItemFont( itemID, m_hPlayerListFont );
  677. pKeyValues->deleteThis();
  678. }
  679. }
  680. m_pPlayerListRed->SetSectionFgColor( 0, g_PR->GetTeamColor( TF_TEAM_RED ) );
  681. m_pPlayerListBlue->SetSectionFgColor( 0, g_PR->GetTeamColor( TF_TEAM_BLUE ) );
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose: Updates the player list
  685. //-----------------------------------------------------------------------------
  686. void CTFHudMatchStatus::UpdatePlayerAvatar( int playerIndex, KeyValues *kv )
  687. {
  688. // Update their avatar
  689. if ( kv && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() )
  690. {
  691. player_info_t pi;
  692. if ( engine->GetPlayerInfo( playerIndex, &pi ) )
  693. {
  694. if ( pi.friendsID )
  695. {
  696. CSteamID steamIDForPlayer( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
  697. // See if we already have that avatar in our list
  698. int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer );
  699. int iImageIndex;
  700. if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() )
  701. {
  702. CAvatarImage *pImage = new CAvatarImage();
  703. pImage->SetAvatarSteamID( steamIDForPlayer );
  704. pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling
  705. iImageIndex = m_pImageList->AddImage( pImage );
  706. m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex );
  707. }
  708. else
  709. {
  710. iImageIndex = m_mapAvatarsToImageList[iMapIndex];
  711. }
  712. kv->SetInt( "avatar", iImageIndex );
  713. CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex );
  714. pAvIm->UpdateFriendStatus();
  715. }
  716. }
  717. }
  718. }
  719. //-----------------------------------------------------------------------------
  720. // Purpose:
  721. //-----------------------------------------------------------------------------
  722. void CTFHudMatchStatus::UpdateTeamInfo()
  723. {
  724. for ( int teamIndex = TF_TEAM_RED; teamIndex <= TF_TEAM_BLUE; teamIndex++ )
  725. {
  726. C_TFTeam *team = GetGlobalTFTeam( teamIndex );
  727. if ( team )
  728. {
  729. // choose dialog variables to set depending on team
  730. const char *pDialogVarTeamName = "";
  731. vgui::EditablePanel *pPanel = NULL;
  732. switch ( teamIndex )
  733. {
  734. case TF_TEAM_RED:
  735. pDialogVarTeamName = "redteamname";
  736. pPanel = m_pRedTeamPanel;
  737. break;
  738. case TF_TEAM_BLUE:
  739. pDialogVarTeamName = "blueteamname";
  740. pPanel = m_pBlueTeamPanel;
  741. break;
  742. default:
  743. Assert( false );
  744. break;
  745. }
  746. // set the team name
  747. if ( pPanel )
  748. {
  749. pPanel->SetDialogVariable( pDialogVarTeamName, team->Get_Localized_Name() );
  750. }
  751. }
  752. }
  753. bool bShowAvatars = g_TF_PR && g_TF_PR->HasPremadeParties();
  754. if ( bShowAvatars )
  755. {
  756. m_pRedLeaderAvatarImage->SetPlayer( GetSteamIDForPlayerIndex( g_TF_PR->GetPartyLeaderRedTeamIndex() ), k_EAvatarSize64x64 );
  757. m_pRedLeaderAvatarImage->SetShouldDrawFriendIcon( false );
  758. m_pBlueLeaderAvatarImage->SetPlayer( GetSteamIDForPlayerIndex( g_TF_PR->GetPartyLeaderBlueTeamIndex() ), k_EAvatarSize64x64 );
  759. m_pBlueLeaderAvatarImage->SetShouldDrawFriendIcon( false );
  760. }
  761. m_pRedLeaderAvatarImage->SetVisible( bShowAvatars );
  762. m_pRedLeaderAvatarBG->SetVisible( bShowAvatars );
  763. m_pRedTeamName->SetVisible( bShowAvatars );
  764. m_pRedTeamImage->SetVisible( !bShowAvatars );
  765. m_pBlueLeaderAvatarImage->SetVisible( bShowAvatars );
  766. m_pBlueLeaderAvatarBG->SetVisible( bShowAvatars );
  767. m_pBlueTeamName->SetVisible( bShowAvatars );
  768. m_pBlueTeamImage->SetVisible( !bShowAvatars );
  769. }