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.

620 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "iclientmode.h"
  8. #include <vgui_controls/AnimationController.h>
  9. #include <vgui_controls/EditablePanel.h>
  10. #include <vgui_controls/ProgressBar.h>
  11. #include "tf_gamerules.h"
  12. #include "tf_logic_halloween_2014.h"
  13. #include "c_tf_playerresource.h"
  14. #include "tf_playerpanel.h"
  15. #include "tf_teamstatus.h"
  16. #include "tf_matchmaking_shared.h"
  17. #include "tf_match_description.h"
  18. using namespace vgui;
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include "tier0/memdbgon.h"
  21. CTFTeamStatusPlayerPanel::CTFTeamStatusPlayerPanel( vgui::Panel *parent, const char *name ) : CTFPlayerPanel( parent, name )
  22. {
  23. m_pHealthBar = new vgui::ContinuousProgressBar( this, "healthbar" );
  24. m_pOverhealBar = new vgui::ContinuousProgressBar( this, "overhealbar" );
  25. m_pClassImageBG = new vgui::Panel( this, "classimagebg" );
  26. m_pDeathFlag = new vgui::ImagePanel( this, "DeathPanel" );
  27. m_iTeam = TEAM_UNASSIGNED;
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Purpose:
  31. //-----------------------------------------------------------------------------
  32. void CTFTeamStatusPlayerPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
  33. {
  34. BaseClass::ApplySchemeSettings( pScheme );
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose:
  38. //-----------------------------------------------------------------------------
  39. void CTFTeamStatusPlayerPanel::UpdateBorder( void )
  40. {
  41. // do nothing
  42. }
  43. //-----------------------------------------------------------------------------
  44. // Purpose:
  45. //-----------------------------------------------------------------------------
  46. bool CTFTeamStatusPlayerPanel::Update( void )
  47. {
  48. if ( !g_TF_PR || !TFGameRules() )
  49. return false;
  50. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  51. if ( !pLocalPlayer || ( pLocalPlayer->GetTeamNumber() < FIRST_GAME_TEAM ) )
  52. return false;
  53. bool bChanged = false;
  54. bool bVisible = GetTeam() >= FIRST_GAME_TEAM;
  55. int iRespawnWait = -1;
  56. if ( IsVisible() != bVisible )
  57. {
  58. SetVisible( bVisible );
  59. bChanged = true;
  60. }
  61. if ( bVisible && g_TF_PR )
  62. {
  63. bool bSameTeamAsLocalPlayer = ( GetTeam() == g_TF_PR->GetTeam( pLocalPlayer->entindex() ) );
  64. // are we connected with a class?
  65. Assert( TF_CLASS_UNDEFINED == 0 );
  66. int iClass = -1;
  67. bool bAlive = false;
  68. int iHealth = -1;
  69. bool bIsLocalPlayer = false;
  70. if ( m_iPlayerIndex > 0 )
  71. {
  72. bIsLocalPlayer = pLocalPlayer->entindex() == m_iPlayerIndex;
  73. iClass = g_TF_PR->GetPlayerClass( m_iPlayerIndex );
  74. if ( iClass != TF_CLASS_UNDEFINED )
  75. {
  76. bAlive = g_TF_PR->IsAlive( m_iPlayerIndex );
  77. }
  78. if ( bAlive )
  79. {
  80. iHealth = g_TF_PR->GetHealth( m_iPlayerIndex );
  81. }
  82. // calc respawn time remaining
  83. if ( !bAlive && ( iClass != TF_CLASS_UNDEFINED ) )
  84. {
  85. float flRespawnAt = g_TF_PR->GetNextRespawnTime( m_iPlayerIndex );
  86. iRespawnWait = ( flRespawnAt - gpGlobals->curtime );
  87. if ( iRespawnWait <= 0 )
  88. iRespawnWait = -1;
  89. }
  90. // hide class info from the other team?
  91. if ( !bSameTeamAsLocalPlayer )
  92. {
  93. iClass = TF_CLASS_UNDEFINED;
  94. }
  95. }
  96. if ( m_iTeam != GetTeam() )
  97. {
  98. m_iTeam = GetTeam();
  99. bChanged = true;
  100. }
  101. if ( m_pClassImageBG )
  102. {
  103. if ( m_iTeam == TF_TEAM_RED )
  104. {
  105. Color bgColor( m_ColorPortraitBGRedLocalPlayer );
  106. if ( !bIsLocalPlayer )
  107. {
  108. bgColor = bAlive ? m_ColorPortraitBGRed : m_ColorPortraitBGRedDead;
  109. }
  110. m_pClassImageBG->SetBgColor( bgColor );
  111. }
  112. else
  113. {
  114. Color bgColor( m_ColorPortraitBGBlueLocalPlayer );
  115. if ( !bIsLocalPlayer )
  116. {
  117. bgColor = bAlive ? m_ColorPortraitBGBlue : m_ColorPortraitBGBlueDead;
  118. }
  119. m_pClassImageBG->SetBgColor( bgColor );
  120. }
  121. }
  122. // update live state
  123. if ( m_pClassImage )
  124. {
  125. if ( m_bPrevAlive != bAlive )
  126. {
  127. bChanged = true;
  128. m_bPrevAlive = bAlive;
  129. if ( !bAlive )
  130. {
  131. m_pClassImage->SetDrawColor( ( m_iTeam == TF_TEAM_RED ) ? m_ColorPortraitBlendDeadRed : m_ColorPortraitBlendDeadBlue );
  132. }
  133. m_pDeathFlag->SetImage( ( m_iTeam == TF_TEAM_RED ) ? "../HUD/comp_player_status" : "../HUD/comp_player_status_blue" );
  134. if ( bAlive )
  135. {
  136. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerAlive", false );
  137. }
  138. else
  139. {
  140. g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerDead", false );
  141. }
  142. }
  143. // update class image
  144. if ( ( m_iPrevClass != iClass ) || bChanged )
  145. {
  146. bChanged = true;
  147. m_iPrevClass = iClass;
  148. if ( iClass < 0 )
  149. {
  150. m_pClassImage->SetImage( "hud_connecting" );
  151. }
  152. else if ( iClass == TF_CLASS_UNDEFINED )
  153. {
  154. int iDeadClass = g_TF_PR->GetPlayerClassWhenKilled( m_iPlayerIndex );
  155. if ( !bAlive && !bSameTeamAsLocalPlayer && ( m_iTeam >= FIRST_GAME_TEAM ) && ( iDeadClass > TF_CLASS_UNDEFINED ) )
  156. {
  157. m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iDeadClass + 9] : g_pszItemClassImagesBlue[iDeadClass + 9] ) );
  158. }
  159. else
  160. {
  161. m_pClassImage->SetImage( "class_portraits/silhouette_alpha" );
  162. }
  163. }
  164. else
  165. {
  166. if ( bAlive )
  167. {
  168. m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass] : g_pszItemClassImagesBlue[iClass] ) );
  169. }
  170. else
  171. {
  172. m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass + 9] : g_pszItemClassImagesBlue[iClass + 9] ) );
  173. }
  174. }
  175. }
  176. }
  177. // update health indicator
  178. if ( m_pHealthBar && m_pOverhealBar )
  179. {
  180. if ( iHealth != m_iPrevHealth )
  181. {
  182. m_iPrevHealth = iHealth;
  183. if ( ( iHealth > 0 ) && bSameTeamAsLocalPlayer )
  184. {
  185. float flMaxHealth = g_TF_PR->GetMaxHealth( m_iPlayerIndex );
  186. float flProgress = iHealth / flMaxHealth;
  187. // health bar
  188. if ( !m_pHealthBar->IsVisible() )
  189. {
  190. m_pHealthBar->SetVisible( true );
  191. }
  192. m_pHealthBar->SetProgress( ( flProgress >= 1.0f ) ? 1.0f : flProgress );
  193. if ( flProgress > m_flPercentageHealthMed )
  194. {
  195. m_pHealthBar->SetFgColor( m_ColorBarHealthHigh );
  196. }
  197. else if ( flProgress > m_flPercentageHealthLow )
  198. {
  199. m_pHealthBar->SetFgColor( m_ColorBarHealthMed );
  200. }
  201. else
  202. {
  203. m_pHealthBar->SetFgColor( m_ColorBarHealthLow );
  204. }
  205. // overheal bar
  206. if ( flProgress > 1.0f )
  207. {
  208. if ( !m_pOverhealBar->IsVisible() )
  209. {
  210. m_pOverhealBar->SetVisible( true );
  211. }
  212. m_pOverhealBar->SetProgress( flProgress - 1.0f );
  213. }
  214. else
  215. {
  216. if ( m_pOverhealBar->IsVisible() )
  217. {
  218. m_pOverhealBar->SetVisible( false );
  219. }
  220. }
  221. }
  222. else
  223. {
  224. if ( m_pHealthBar->IsVisible() )
  225. {
  226. m_pHealthBar->SetVisible( false );
  227. }
  228. if ( m_pOverhealBar->IsVisible() )
  229. {
  230. m_pOverhealBar->SetVisible( false );
  231. }
  232. }
  233. bChanged = true;
  234. }
  235. }
  236. // update respawn time
  237. if ( iRespawnWait != m_iPrevRespawnWait )
  238. {
  239. m_iPrevRespawnWait = iRespawnWait;
  240. if ( ( iRespawnWait < 0 ) || !bSameTeamAsLocalPlayer )
  241. {
  242. SetDialogVariable( "respawntime", "" );
  243. }
  244. else
  245. {
  246. SetDialogVariable( "respawntime", VarArgs( "%d", iRespawnWait ) );
  247. }
  248. bChanged = true;
  249. }
  250. // gamerules state
  251. if ( TFGameRules()->State_Get() != m_iPrevState )
  252. {
  253. m_iPrevState = TFGameRules()->State_Get();
  254. bChanged = true;
  255. }
  256. }
  257. return bChanged;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Constructor
  261. //-----------------------------------------------------------------------------
  262. CTFTeamStatus::CTFTeamStatus( Panel *parent, const char *panelName ) : BaseClass( parent, panelName )
  263. {
  264. m_pPlayerPanelKVs = NULL;
  265. m_bReapplyPlayerPanelKVs = false;
  266. Reset();
  267. ivgui()->AddTickSignal( GetVPanel(), 100 );
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. CTFTeamStatus::~CTFTeamStatus()
  273. {
  274. if ( m_pPlayerPanelKVs )
  275. {
  276. m_pPlayerPanelKVs->deleteThis();
  277. m_pPlayerPanelKVs = NULL;
  278. }
  279. }
  280. void SetGrowDir( CTFTeamStatus::EGrowDir* pGrowDir, const char* pszString )
  281. {
  282. if ( FStrEq( pszString, "east" ) )
  283. {
  284. (*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_EAST;
  285. }
  286. else if ( FStrEq( pszString, "west" ) )
  287. {
  288. (*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_WEST;
  289. }
  290. else
  291. {
  292. Assert( !"Invalid direction string for team status team grow direction" );
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose:
  297. //-----------------------------------------------------------------------------
  298. void CTFTeamStatus::ApplySettings( KeyValues *inResourceData )
  299. {
  300. BaseClass::ApplySettings( inResourceData );
  301. SetGrowDir( &m_eTeam1GrowDir, inResourceData->GetString( "team1_grow_dir" ) );
  302. SetGrowDir( &m_eTeam2GrowDir, inResourceData->GetString( "team2_grow_dir" ) );
  303. KeyValues *pItemKV = inResourceData->FindKey( "playerpanels_kv" );
  304. if ( pItemKV )
  305. {
  306. if ( m_pPlayerPanelKVs )
  307. {
  308. m_pPlayerPanelKVs->deleteThis();
  309. }
  310. m_pPlayerPanelKVs = new KeyValues( "playerpanels_kv" );
  311. pItemKV->CopySubkeys( m_pPlayerPanelKVs );
  312. }
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose:
  316. //-----------------------------------------------------------------------------
  317. void CTFTeamStatus::ApplySchemeSettings( IScheme *pScheme )
  318. {
  319. BaseClass::ApplySchemeSettings( pScheme );
  320. m_bReapplyPlayerPanelKVs = true;
  321. }
  322. //-----------------------------------------------------------------------------
  323. // Purpose:
  324. //-----------------------------------------------------------------------------
  325. void CTFTeamStatus::PerformLayout( void )
  326. {
  327. BaseClass::PerformLayout();
  328. int iTeam1Count = 0;
  329. int iTeam2Count = 0;
  330. // Tally up how many are on each team so we can scale them appropriately down below
  331. for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
  332. {
  333. if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_BLUE )
  334. {
  335. ++iTeam1Count;
  336. }
  337. else if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_RED )
  338. {
  339. ++iTeam2Count;
  340. }
  341. }
  342. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  343. // Snag local player info
  344. int nLocalPlayerTeam = TF_TEAM_COUNT;
  345. int nLocalPlayerIndex = -1;
  346. if ( pLocalPlayer )
  347. {
  348. nLocalPlayerIndex = pLocalPlayer->entindex();
  349. nLocalPlayerTeam = pLocalPlayer->GetTeamNumber();
  350. }
  351. int iTeam1Processed = 0;
  352. int iTeam2Processed = 0;
  353. for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
  354. {
  355. if ( m_PlayerPanels[i]->GetPlayerIndex() <= 0 )
  356. {
  357. m_PlayerPanels[i]->SetVisible( false );
  358. continue;
  359. }
  360. bool bIsLocalPlayerPanel = nLocalPlayerIndex == m_PlayerPanels[i]->GetPlayerIndex();
  361. int iTeam = m_PlayerPanels[i]->GetTeam();
  362. int iXPos = 0;
  363. // Setup vars
  364. EGrowDir& eGrowDir = iTeam == TF_TEAM_BLUE ? m_eTeam1GrowDir : m_eTeam2GrowDir;
  365. int& iTeamCount = iTeam == TF_TEAM_BLUE ? iTeam1Count : iTeam2Count;
  366. int& iBaseX = iTeam == TF_TEAM_BLUE ? m_iTeam1BaseX : m_iTeam2BaseX;
  367. int& iProcessed = iTeam == TF_TEAM_BLUE ? iTeam1Processed : iTeam2Processed;
  368. int& iMaxExpand = iTeam == TF_TEAM_BLUE ? m_iTeam1MaxExpand : m_iTeam2MaxExpand;
  369. const int iGap = RemapValClamped( iTeamCount, 6, 12, m_i6v6Gap, m_i12v12Gap );
  370. // Local player is always the innermost panel
  371. int nTeamPanelIndex = bIsLocalPlayerPanel ? 0
  372. : iTeam == nLocalPlayerTeam ? iProcessed + 1
  373. : iProcessed;
  374. // Setup X-position and widths
  375. // Use the max width if less than 6 (to fill out the space)
  376. int nNewWide = iTeamCount <= 6 ? m_iMaxSize : ( iMaxExpand - ( iGap * ( iTeamCount - 1 ) ) ) / iTeamCount;
  377. // How far each panel is apart from each other
  378. const int iXStep = iGap + nNewWide;
  379. // How many steps out this panel should be
  380. const int iXOffset = iXStep * nTeamPanelIndex;
  381. // Step out the panels by the step
  382. iXPos = eGrowDir == GROW_EAST ? iBaseX + iXOffset // Align the left edge of the left-most panel on the specified point
  383. : eGrowDir == GROW_WEST ? iBaseX - iXOffset - nNewWide // Align the right edge of the right-most panel on the specified point
  384. : 0;
  385. // This is expensive, so only do it if we need to
  386. if ( nNewWide != m_PlayerPanels[i]->GetWide() )
  387. {
  388. // We change the width in the KV's then reapply settings so the children will do their
  389. // magical proportionaltoparent layout.
  390. if ( m_pPlayerPanelKVs )
  391. {
  392. m_pPlayerPanelKVs->SetInt( "wide", scheme()->GetProportionalNormalizedValueEx( GetScheme(), nNewWide ) );
  393. m_PlayerPanels[i]->ApplySettings( m_pPlayerPanelKVs );
  394. m_PlayerPanels[i]->InvalidateLayout( false, true );
  395. m_PlayerPanels[i]->Update();
  396. }
  397. }
  398. if ( !bIsLocalPlayerPanel )
  399. {
  400. ++iProcessed;
  401. }
  402. m_PlayerPanels[i]->SetPos( iXPos, m_PlayerPanels[i]->GetYPos() );
  403. }
  404. }
  405. //-----------------------------------------------------------------------------
  406. // Purpose:
  407. //-----------------------------------------------------------------------------
  408. void CTFTeamStatus::Reset()
  409. {
  410. for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
  411. {
  412. m_PlayerPanels[i]->Reset();
  413. }
  414. InvalidateLayout();
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Purpose:
  418. //-----------------------------------------------------------------------------
  419. bool CTFTeamStatus::ShouldDraw( void )
  420. {
  421. // Get the player and active weapon.
  422. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
  423. if ( !pPlayer )
  424. return false;
  425. int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() );
  426. if ( iLocalTeam < FIRST_GAME_TEAM )
  427. return false;
  428. if ( TFGameRules() )
  429. {
  430. const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() );
  431. if ( !pMatchDesc || !pMatchDesc->m_params.m_bUseMatchHud )
  432. return false;
  433. if ( TFGameRules()->ShowMatchSummary() )
  434. return false;
  435. }
  436. return true;
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose:
  440. //-----------------------------------------------------------------------------
  441. void CTFTeamStatus::OnTick()
  442. {
  443. if ( IsVisible() != ShouldDraw() )
  444. {
  445. SetVisible( ShouldDraw() );
  446. }
  447. if ( IsVisible() )
  448. {
  449. RecalculatePlayerPanels();
  450. }
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Purpose:
  454. //-----------------------------------------------------------------------------
  455. CTFTeamStatusPlayerPanel *CTFTeamStatus::GetOrAddPanel( int iPanelIndex )
  456. {
  457. if ( iPanelIndex < m_PlayerPanels.Count() )
  458. {
  459. return m_PlayerPanels[iPanelIndex];
  460. }
  461. Assert( iPanelIndex == m_PlayerPanels.Count() );
  462. CTFTeamStatusPlayerPanel *pPanel = new CTFTeamStatusPlayerPanel( this, VarArgs( "playerpanel%d", iPanelIndex ) );
  463. if ( m_pPlayerPanelKVs )
  464. {
  465. pPanel->ApplySettings( m_pPlayerPanelKVs );
  466. pPanel->InvalidateLayout( false, true );
  467. InvalidateLayout();
  468. }
  469. m_PlayerPanels.AddToTail( pPanel );
  470. return pPanel;
  471. }
  472. //-----------------------------------------------------------------------------
  473. // Purpose:
  474. //-----------------------------------------------------------------------------
  475. void CTFTeamStatus::RecalculatePlayerPanels( void )
  476. {
  477. C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
  478. if ( !pPlayer || !g_TF_PR )
  479. return;
  480. int iPanel = 0;
  481. bool bNeedsLayout = false;
  482. int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() );
  483. if ( iLocalTeam >= FIRST_GAME_TEAM )
  484. {
  485. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  486. {
  487. if ( !g_TF_PR->IsConnected( i ) )
  488. continue;
  489. int iTeam = g_TF_PR->GetTeam( i );
  490. if ( iTeam < FIRST_GAME_TEAM )
  491. continue;
  492. // Add an entry
  493. CTFTeamStatusPlayerPanel *pPanel = GetOrAddPanel( iPanel );
  494. if ( pPanel->GetPlayerIndex() != i )
  495. {
  496. bNeedsLayout = true;
  497. }
  498. pPanel->SetPlayerIndex( i );
  499. if ( pPanel->GetPreviousTeam() != pPanel->GetTeam() )
  500. {
  501. bNeedsLayout = true;
  502. }
  503. ++iPanel;
  504. }
  505. }
  506. // Clear out any extra panels
  507. for ( int i = iPanel; i < m_PlayerPanels.Count(); i++ )
  508. {
  509. if ( m_PlayerPanels[i]->GetPlayerIndex() != 0 )
  510. {
  511. bNeedsLayout = true;
  512. }
  513. m_PlayerPanels[i]->SetPlayerIndex( 0 );
  514. }
  515. UpdatePlayerPanels();
  516. if ( bNeedsLayout )
  517. {
  518. InvalidateLayout();
  519. }
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose:
  523. //-----------------------------------------------------------------------------
  524. void CTFTeamStatus::UpdatePlayerPanels( void )
  525. {
  526. if ( !g_TF_PR )
  527. return;
  528. for ( int i = 0; i < m_PlayerPanels.Count(); i++ )
  529. {
  530. m_PlayerPanels[i]->Update();
  531. }
  532. }