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.

1564 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: HUD Target ID element
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "tf_hud_target_id.h"
  9. #include "c_tf_playerresource.h"
  10. #include "iclientmode.h"
  11. #include "vgui/ILocalize.h"
  12. #include "c_baseobject.h"
  13. #include "c_team.h"
  14. #include "tf_gamerules.h"
  15. #include "tf_hud_statpanel.h"
  16. #if defined( REPLAY_ENABLED )
  17. #include "replay/iclientreplaycontext.h"
  18. #include "replay/ireplaymoviemanager.h"
  19. #include "replay/ienginereplay.h"
  20. #endif // REPLAY_ENABLED
  21. #include "tf_weapon_bonesaw.h"
  22. #include "sourcevr/isourcevirtualreality.h"
  23. #include "tf_revive.h"
  24. #include "tf_logic_robot_destruction.h"
  25. #include "entity_capture_flag.h"
  26. #include "vgui_avatarimage.h"
  27. #include "VGuiMatSurface/IMatSystemSurface.h"
  28. #include "renderparm.h"
  29. #include "tf_dropped_weapon.h"
  30. #include "econ/econ_item_description.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. extern ConVar cl_hud_minmode;
  34. DECLARE_HUDELEMENT( CMainTargetID );
  35. DECLARE_HUDELEMENT( CSpectatorTargetID );
  36. DECLARE_HUDELEMENT( CSecondaryTargetID );
  37. using namespace vgui;
  38. enum
  39. {
  40. SPECTATOR_TARGET_ID_NORMAL = 0,
  41. SPECTATOR_TARGET_ID_BOTTOM_LEFT,
  42. SPECTATOR_TARGET_ID_BOTTOM_CENTER,
  43. SPECTATOR_TARGET_ID_BOTTOM_RIGHT,
  44. };
  45. void SpectatorTargetLocationCallback( IConVar *var, const char *oldString, float oldFloat )
  46. {
  47. CSpectatorTargetID *pSpecTargetID = (CSpectatorTargetID *)GET_HUDELEMENT( CSpectatorTargetID );
  48. if ( pSpecTargetID )
  49. {
  50. pSpecTargetID->InvalidateLayout();
  51. }
  52. }
  53. ConVar tf_spectator_target_location( "tf_spectator_target_location", "0", FCVAR_ARCHIVE, "Determines the location of the spectator targetID panel.", true, 0, true, 3, SpectatorTargetLocationCallback );
  54. ConVar tf_hud_target_id_disable_floating_health( "tf_hud_target_id_disable_floating_health", "0", FCVAR_ARCHIVE, "Set to disable floating health bar" );
  55. ConVar tf_hud_target_id_alpha( "tf_hud_target_id_alpha", "100", FCVAR_ARCHIVE, "Alpha value of target id background, default 100" );
  56. ConVar tf_hud_target_id_offset( "tf_hud_target_id_offset", "0", FCVAR_ARCHIVE, "RES file Y offset for target id" );
  57. ConVar tf_hud_target_id_show_avatars( "tf_hud_target_id_show_avatars", "2", FCVAR_ARCHIVE, "Display Steam avatars on TargetID when using floating health icons. 1 = everyone, 2 = friends only." );
  58. #ifdef STAGING_ONLY
  59. ConVar tf_bountymode_showhealth( "tf_bountymode_showhealth", "0", FCVAR_ARCHIVE, "Show floating health icon over enemy players. 1 = show health, 2 = show health and level", true, 0, true, 2 );
  60. #endif // STAGING_ONLY
  61. bool ShouldHealthBarBeVisible( CBaseEntity *pTarget, CTFPlayer *pLocalPlayer )
  62. {
  63. if ( !pTarget || !pLocalPlayer )
  64. return false;
  65. if ( tf_hud_target_id_disable_floating_health.GetBool() )
  66. return false;
  67. if ( pTarget->IsHealthBarVisible() )
  68. return true;
  69. if ( !pTarget->IsPlayer() )
  70. return false;
  71. if ( pLocalPlayer->IsPlayerClass( TF_CLASS_SPY ) )
  72. return true;
  73. if ( pLocalPlayer->InSameTeam( pTarget ) )
  74. return true;
  75. if ( pLocalPlayer->InSameDisguisedTeam( pTarget ) )
  76. return true;
  77. int iSeeEnemyHealth = 0;
  78. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalPlayer, iSeeEnemyHealth, see_enemy_health )
  79. if ( iSeeEnemyHealth )
  80. return true;
  81. return false;
  82. }
  83. //-----------------------------------------------------------------------------
  84. // Purpose:
  85. //-----------------------------------------------------------------------------
  86. CTargetID::CTargetID( const char *pElementName ) :
  87. CHudElement( pElementName ), BaseClass( NULL, pElementName )
  88. {
  89. vgui::Panel *pParent = g_pClientMode->GetViewport();
  90. SetParent( pParent );
  91. m_hFont = g_hFontTrebuchet24;
  92. m_flLastChangeTime = 0;
  93. m_iLastEntIndex = 0;
  94. m_nOriginalY = 0;
  95. m_bArenaPanelVisible = false;
  96. SetHiddenBits( HIDEHUD_MISCSTATUS );
  97. m_pTargetNameLabel = NULL;
  98. m_pTargetDataLabel = NULL;
  99. m_pBGPanel = NULL;
  100. m_pMoveableIcon = NULL;
  101. m_pMoveableSymbolIcon = NULL;
  102. m_pMoveableIconBG = NULL;
  103. m_pMoveableKeyLabel = NULL;
  104. m_pTargetHealth = new CTFSpectatorGUIHealth( this, "SpectatorGUIHealth" );
  105. m_pTargetAmmoIcon = NULL;
  106. m_pTargetKillStreakIcon = NULL;
  107. m_bLayoutOnUpdate = false;
  108. m_pFloatingHealthIcon = NULL;
  109. m_iLastScannedEntIndex = 0;
  110. m_pAvatarImage = NULL;
  111. RegisterForRenderGroup( "mid" );
  112. RegisterForRenderGroup( "commentary" );
  113. m_iRenderPriority = 5;
  114. ListenForGameEvent( "show_class_layout" );
  115. RegisterForRenderGroup( "arena_target_id" );
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. //-----------------------------------------------------------------------------
  120. void CTargetID::LevelShutdown( void )
  121. {
  122. if ( m_pFloatingHealthIcon )
  123. {
  124. m_pFloatingHealthIcon->MarkForDeletion();
  125. m_pFloatingHealthIcon = NULL;
  126. }
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose: Setup
  130. //-----------------------------------------------------------------------------
  131. void CTargetID::Reset( void )
  132. {
  133. m_pTargetHealth->Reset();
  134. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  135. if ( pScheme )
  136. {
  137. m_LabelColorDefault = pScheme->GetColor( "Label.TextColor", Color( 255, 255, 255, 255 ) );
  138. }
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. //-----------------------------------------------------------------------------
  143. void CTargetID::FireGameEvent( IGameEvent * event )
  144. {
  145. const char *eventName = event->GetName();
  146. if ( FStrEq( "show_class_layout", eventName ) )
  147. {
  148. if ( TFGameRules() && TFGameRules()->IsInArenaMode() && GetLocalPlayerTeam() > LAST_SHARED_TEAM )
  149. {
  150. m_bArenaPanelVisible = event->GetBool( "show", false );
  151. }
  152. else
  153. {
  154. m_bArenaPanelVisible = false;
  155. }
  156. InvalidateLayout( true );
  157. }
  158. }
  159. //-----------------------------------------------------------------------------
  160. bool CTargetID::DrawHealthIcon()
  161. {
  162. C_BaseEntity *pEnt = cl_entitylist->GetEnt( GetTargetIndex() );
  163. if ( pEnt && pEnt->IsBaseObject() )
  164. return true;
  165. if ( tf_hud_target_id_disable_floating_health.GetBool() )
  166. return true;
  167. return false;
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose: Find out which player to pull an avatar image from. pTFPlayer is the player under the crosshair.
  171. //-----------------------------------------------------------------------------
  172. C_TFPlayer *CTargetID::GetTargetForSteamAvatar( C_TFPlayer *pTFPlayer )
  173. {
  174. if ( !tf_hud_target_id_show_avatars.GetBool() )
  175. return NULL;
  176. if ( !pTFPlayer || ( g_TF_PR && g_TF_PR->IsFakePlayer( pTFPlayer->entindex() ) ) )
  177. return NULL;
  178. C_TFPlayer *pTFLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  179. if ( !pTFLocalPlayer )
  180. return NULL;
  181. // Health icon inside the panel (too busy - figure this out later)
  182. if ( DrawHealthIcon() )
  183. return NULL;
  184. // Save room when healing or being healed
  185. if ( pTFLocalPlayer->IsPlayerClass( TF_CLASS_MEDIC ) && pTFLocalPlayer->MedicGetHealTarget() == pTFPlayer )
  186. return NULL;
  187. C_TFPlayer *pTFHealer = NULL;
  188. float flHealerChargeLevel = -1.f;
  189. pTFLocalPlayer->GetHealer( &pTFHealer, &flHealerChargeLevel );
  190. if ( pTFHealer && pTFHealer->entindex() == m_iTargetEntIndex )
  191. return NULL;
  192. if ( pTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  193. {
  194. C_TFPlayer *pDisguiseTarget = ToTFPlayer( pTFPlayer->m_Shared.GetDisguiseTarget() );
  195. if ( pDisguiseTarget && ( pTFLocalPlayer->InSameTeam( pDisguiseTarget ) || pDisguiseTarget == pTFLocalPlayer ) )
  196. {
  197. // Bots don't (currently) have avatars.
  198. if ( pDisguiseTarget->IsBot() )
  199. return NULL;
  200. if ( tf_hud_target_id_show_avatars.GetInt() == 2 && !pTFLocalPlayer->IsPlayerOnSteamFriendsList( pDisguiseTarget ) )
  201. return NULL;
  202. return pDisguiseTarget;
  203. }
  204. }
  205. if ( pTFLocalPlayer->IsPlayerOnSteamFriendsList( pTFPlayer ) )
  206. return pTFPlayer;
  207. if ( tf_hud_target_id_show_avatars.GetInt() == 1 )
  208. return pTFPlayer;
  209. return NULL;
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose:
  213. //-----------------------------------------------------------------------------
  214. void CTargetID::ApplySchemeSettings( vgui::IScheme *scheme )
  215. {
  216. LoadControlSettings( "resource/UI/TargetID.res" );
  217. BaseClass::ApplySchemeSettings( scheme );
  218. m_pTargetNameLabel = dynamic_cast<Label *>(FindChildByName("TargetNameLabel"));
  219. m_pTargetDataLabel = dynamic_cast<Label *>(FindChildByName("TargetDataLabel"));
  220. m_pBGPanel = dynamic_cast<CTFImagePanel *> ( FindChildByName("TargetIDBG") );
  221. m_pMoveableSubPanel = dynamic_cast<vgui::EditablePanel *> ( FindChildByName("MoveableSubPanel") );
  222. if ( m_pMoveableSubPanel )
  223. {
  224. m_pMoveableIcon = dynamic_cast<CIconPanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableIcon") );
  225. m_pMoveableSymbolIcon = dynamic_cast<vgui::ImagePanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableSymbolIcon") );
  226. m_pMoveableIconBG = dynamic_cast<CIconPanel *> ( m_pMoveableSubPanel->FindChildByName("MoveableIconBG") );
  227. m_pMoveableKeyLabel = dynamic_cast<Label *>( m_pMoveableSubPanel->FindChildByName("MoveableKeyLabel") );
  228. }
  229. m_hFont = scheme->GetFont( "TargetID", true );
  230. m_pTargetAmmoIcon = dynamic_cast<vgui::ImagePanel *>( FindChildByName( "AmmoIcon" ) );
  231. m_pTargetKillStreakIcon = dynamic_cast<vgui::ImagePanel *>( FindChildByName( "KillStreakIcon" ) );
  232. m_pAvatarImage = dynamic_cast< CAvatarImagePanel* >( FindChildByName( "AvatarImage" ) );
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose:
  236. //-----------------------------------------------------------------------------
  237. void CTargetID::ApplySettings( KeyValues *inResourceData )
  238. {
  239. BaseClass::ApplySettings( inResourceData );
  240. m_iRenderPriority = inResourceData->GetInt( "priority" );
  241. int x;
  242. GetPos( x, m_nOriginalY );
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose:
  246. //-----------------------------------------------------------------------------
  247. int CTargetID::GetRenderGroupPriority( void )
  248. {
  249. return m_iRenderPriority;
  250. }
  251. //-----------------------------------------------------------------------------
  252. // Purpose:
  253. //-----------------------------------------------------------------------------
  254. void CTargetID::UpdateFloatingHealthIconVisibility( bool bVisible )
  255. {
  256. if ( m_pFloatingHealthIcon && ( m_pFloatingHealthIcon->IsVisible() != bVisible ) )
  257. {
  258. m_pFloatingHealthIcon->SetVisible( bVisible );
  259. }
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: clear out string etc between levels
  263. //-----------------------------------------------------------------------------
  264. void CTargetID::VidInit()
  265. {
  266. CHudElement::VidInit();
  267. m_flLastChangeTime = 0;
  268. m_iLastEntIndex = 0;
  269. }
  270. bool CTargetID::IsValidIDTarget( int nEntIndex, float flOldTargetRetainFOV, float &flNewTargetRetainFOV )
  271. {
  272. bool bReturn = false;
  273. flNewTargetRetainFOV = 0.0f;
  274. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  275. if ( !pLocalTFPlayer )
  276. return false;
  277. #ifdef STAGING_ONLY
  278. if ( pLocalTFPlayer->m_Shared.InCond( TF_COND_STEALTHED_PHASE ) )
  279. return false;
  280. #endif // STAGING_ONLY
  281. if ( nEntIndex )
  282. {
  283. C_BaseEntity *pEnt = cl_entitylist->GetEnt( nEntIndex );
  284. if ( pEnt )
  285. {
  286. Vector vDiff = pEnt->EyePosition() - pLocalTFPlayer->EyePosition();
  287. float flDist;
  288. flDist = VectorNormalize( vDiff );
  289. if ( flOldTargetRetainFOV != 0.0f )
  290. {
  291. // It has a FOV that maintains previous targets
  292. Vector vForward;
  293. pLocalTFPlayer->EyeVectors( &vForward );
  294. float fAngle = 1.0f - vDiff.Dot( vForward );
  295. fAngle = RemapVal( fAngle, 0.0f, 1.0f, 0.0f, 90.0f );
  296. if ( fAngle > flOldTargetRetainFOV )
  297. {
  298. return false;
  299. }
  300. }
  301. C_TFPlayer *pPlayer = ToTFPlayer( pEnt );
  302. int iHideEnemyHealth = 0;
  303. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalTFPlayer, iHideEnemyHealth, hide_enemy_health );
  304. bool bInSameTeam = pLocalTFPlayer->InSameDisguisedTeam( pEnt );
  305. bool bSpy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && iHideEnemyHealth == 0;
  306. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  307. {
  308. // We don't want to show health bars to the spy in MVM because it's distracting
  309. bSpy = false;
  310. // Are we disguised as the enemy?
  311. if ( pLocalTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pLocalTFPlayer->m_Shared.GetDisguiseTeam() != pLocalTFPlayer->GetTeamNumber() )
  312. {
  313. // Get the target's apparent team
  314. int iTheirApparentTeam = pEnt->GetTeamNumber();
  315. if ( pPlayer && pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  316. {
  317. iTheirApparentTeam = pPlayer->m_Shared.GetDisguiseTeam();
  318. }
  319. // Are we disguised as they appear?
  320. if ( pLocalTFPlayer->m_Shared.GetDisguiseTeam() == iTheirApparentTeam )
  321. {
  322. // Don't show the health
  323. bInSameTeam = false;
  324. }
  325. }
  326. }
  327. bool bSpectator = pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR;
  328. int iSeeEnemyHealth = 0;
  329. bool bStealthed = false;
  330. bool bHealthBarVisible = ShouldHealthBarBeVisible( pEnt, pLocalTFPlayer );
  331. bool bShow = bHealthBarVisible;
  332. if ( pPlayer )
  333. {
  334. if ( pPlayer->m_Shared.IsStealthed() )
  335. {
  336. bStealthed = true;
  337. bHealthBarVisible = false;
  338. bShow = false;
  339. }
  340. if ( !bStealthed )
  341. {
  342. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pLocalTFPlayer, iSeeEnemyHealth, see_enemy_health );
  343. }
  344. bool bMaintainInFOV = !pLocalTFPlayer->InSameTeam( pEnt );
  345. if ( bHealthBarVisible )
  346. {
  347. bool bEnemyPlayer = pPlayer->GetTeamNumber() != pLocalTFPlayer->GetTeamNumber();
  348. bool bEnemyMiniBoss = pPlayer->IsMiniBoss() && bEnemyPlayer;
  349. bShow = bEnemyMiniBoss;
  350. #ifdef STAGING_ONLY
  351. bShow |= TFGameRules() && TFGameRules()->IsBountyMode() && tf_bountymode_showhealth.GetInt() && bEnemyPlayer;
  352. #endif // STAGING_ONLY
  353. if ( bShow )
  354. {
  355. bMaintainInFOV = false;
  356. // Minibosses keep the health indicator up within a small FOV until a different valid target is selected
  357. // The FOV needs to grow exponentially when a target is getting near
  358. if ( bEnemyMiniBoss )
  359. {
  360. bMaintainInFOV = true;
  361. }
  362. }
  363. }
  364. if ( bMaintainInFOV )
  365. {
  366. const float flMaxDist = 800.0f;
  367. float fInterp = RemapVal( flMaxDist - MIN( flDist, flMaxDist ), 0.0f, flMaxDist, 0.0f, 1.0f );
  368. fInterp *= fInterp;
  369. flNewTargetRetainFOV = fInterp * 13.0f + 0.75f;
  370. }
  371. bReturn = ( bSpectator || pLocalTFPlayer->InSameTeam( pEnt ) || ( ( bInSameTeam || bSpy || iSeeEnemyHealth ) && !bStealthed ) );
  372. }
  373. if ( bShow || bHealthBarVisible )
  374. {
  375. // See if we're re-targeting our previous
  376. if ( m_pFloatingHealthIcon )
  377. {
  378. if ( m_pFloatingHealthIcon->GetEntity() && m_pFloatingHealthIcon->GetEntity() == pEnt )
  379. {
  380. UpdateFloatingHealthIconVisibility( true );
  381. }
  382. else
  383. {
  384. // New target - clear previous
  385. m_pFloatingHealthIcon->MarkForDeletion();
  386. m_pFloatingHealthIcon = NULL;
  387. }
  388. }
  389. //Recreate the floating health icon if there isn't one, we're not a spectator, and
  390. // we're not a spy or this was a robot from Robot Destruction-Mode
  391. if ( !m_pFloatingHealthIcon && !bSpectator && ( !bSpy || bHealthBarVisible ) && !DrawHealthIcon() )
  392. {
  393. m_pFloatingHealthIcon = CFloatingHealthIcon::AddFloatingHealthIcon( pEnt );
  394. }
  395. }
  396. else if ( pEnt->IsBaseObject() && ( bInSameTeam || bSpy ) )
  397. {
  398. bReturn = true;
  399. }
  400. else if ( pEnt->IsVisibleToTargetID() )
  401. {
  402. bReturn = true;
  403. }
  404. else
  405. {
  406. UpdateFloatingHealthIconVisibility( false );
  407. }
  408. }
  409. }
  410. return bReturn;
  411. }
  412. //-----------------------------------------------------------------------------
  413. // Purpose:
  414. //-----------------------------------------------------------------------------
  415. bool CTargetID::ShouldDraw( void )
  416. {
  417. if ( !CHudElement::ShouldDraw() )
  418. {
  419. UpdateFloatingHealthIconVisibility( false );
  420. return false;
  421. }
  422. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  423. {
  424. UpdateFloatingHealthIconVisibility( false );
  425. return false;
  426. }
  427. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  428. if ( !pLocalTFPlayer )
  429. {
  430. UpdateFloatingHealthIconVisibility( false );
  431. return false;
  432. }
  433. if ( pLocalTFPlayer->IsTaunting() )
  434. {
  435. UpdateFloatingHealthIconVisibility( false );
  436. return false;
  437. }
  438. // Get our target's ent index
  439. m_iTargetEntIndex = CalculateTargetIndex(pLocalTFPlayer);
  440. if ( !m_iTargetEntIndex )
  441. {
  442. if ( m_flTargetRetainFOV == 0.0f )
  443. {
  444. // Check to see if we should clear our ID
  445. if ( m_flLastChangeTime && ( gpGlobals->curtime > m_flLastChangeTime ) )
  446. {
  447. m_flLastChangeTime = 0;
  448. m_iLastEntIndex = 0;
  449. }
  450. else
  451. {
  452. // Keep re-using the old one
  453. m_iTargetEntIndex = m_iLastEntIndex;
  454. }
  455. }
  456. // If we're showing a floating health icon, and no longer have a target,
  457. // hide it and see if it's the same entity next time
  458. UpdateFloatingHealthIconVisibility( false );
  459. }
  460. else
  461. {
  462. m_flLastChangeTime = gpGlobals->curtime;
  463. if ( m_iTargetEntIndex != m_iLastScannedEntIndex )
  464. {
  465. // If we switched to another, valid target for a floating health icon, recreate it on the next pass
  466. if ( m_pFloatingHealthIcon )
  467. {
  468. m_pFloatingHealthIcon->MarkForDeletion();
  469. m_pFloatingHealthIcon = NULL;
  470. }
  471. m_iLastScannedEntIndex = m_iTargetEntIndex;
  472. }
  473. }
  474. float flTargetRetainFOV = 0.0f;
  475. bool bReturn = IsValidIDTarget( m_iTargetEntIndex, 0.0f, flTargetRetainFOV );
  476. if ( !bReturn )
  477. {
  478. m_iLastEntIndex = 0;
  479. }
  480. else
  481. {
  482. if ( !IsVisible() || (m_iTargetEntIndex != m_iLastEntIndex) )
  483. {
  484. m_iLastEntIndex = m_iTargetEntIndex;
  485. m_bLayoutOnUpdate = true;
  486. m_flTargetRetainFOV = flTargetRetainFOV;
  487. if ( m_pAvatarImage )
  488. {
  489. m_pAvatarImage->SetVisible( false );
  490. }
  491. }
  492. UpdateID();
  493. }
  494. return bReturn;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose:
  498. //-----------------------------------------------------------------------------
  499. void CTargetID::PerformLayout( void )
  500. {
  501. int iXIndent = XRES(5);
  502. int iXPostdent = XRES(10);
  503. int iWidth = iXIndent + iXPostdent;
  504. if ( DrawHealthIcon() )
  505. {
  506. iWidth += m_pTargetHealth->GetWide();
  507. }
  508. if ( m_pAvatarImage && m_pAvatarImage->IsVisible() )
  509. {
  510. iWidth += m_pAvatarImage->GetWide() + XRES( 2 );
  511. }
  512. int iTextW, iTextH;
  513. int iDataW, iDataH;
  514. if ( m_pTargetNameLabel && m_pTargetDataLabel )
  515. {
  516. m_pTargetNameLabel->GetContentSize( iTextW, iTextH );
  517. m_pTargetDataLabel->GetContentSize( iDataW, iDataH );
  518. iWidth += MAX(iTextW,iDataW);
  519. if ( m_pBGPanel )
  520. {
  521. m_pBGPanel->SetSize( iWidth, GetTall() );
  522. }
  523. int x1 = 0, y1 = 0;
  524. int x2 = 0, y2 = 0;
  525. int x3 = 0, y3 = 0;
  526. m_pTargetNameLabel->GetPos( x1, y1 );
  527. m_pTargetDataLabel->GetPos( x2, y2 );
  528. if ( m_pTargetKillStreakIcon )
  529. {
  530. m_pTargetKillStreakIcon->GetPos( x3, y3 );
  531. }
  532. int iWideExtra = 0;
  533. if ( DrawHealthIcon() )
  534. {
  535. iWideExtra += m_pTargetHealth->GetWide();
  536. }
  537. if ( m_pAvatarImage && m_pAvatarImage->IsVisible() )
  538. {
  539. iWideExtra += m_pAvatarImage->GetWide() + XRES( 4 );
  540. }
  541. int nBuffer = ( m_pAvatarImage && m_pAvatarImage->IsVisible() ) ? 6 : 8;
  542. m_pTargetNameLabel->SetPos( XRES( nBuffer ) + iWideExtra, y1 );
  543. m_pTargetDataLabel->SetPos( XRES( nBuffer ) + iWideExtra, y2 );
  544. if ( m_pTargetKillStreakIcon )
  545. {
  546. int nKSBuffer = ( cl_hud_minmode.GetBool() ) ? 6 : 9;
  547. m_pTargetKillStreakIcon->SetPos( XRES( nKSBuffer ) + iWideExtra, y3 );
  548. }
  549. }
  550. // Put the moveable icon to the right hand of our panel
  551. if ( m_pMoveableSubPanel && m_pMoveableSubPanel->IsVisible() )
  552. {
  553. if ( m_pMoveableKeyLabel && m_pMoveableIcon && m_pMoveableSymbolIcon && m_pMoveableIconBG )
  554. {
  555. m_pMoveableKeyLabel->SizeToContents();
  556. int iIndent = XRES(4);
  557. int iMoveWide = MAX( XRES(16) + m_pMoveableKeyLabel->GetWide() + iIndent, (m_pMoveableIcon->GetWide()) + iIndent + XRES(8) );
  558. m_pMoveableKeyLabel->SetWide( iMoveWide );
  559. m_pMoveableSubPanel->SetSize( iMoveWide, GetTall() );
  560. m_pMoveableSubPanel->SetPos( iWidth - iIndent, 0 );
  561. int x,y;
  562. m_pMoveableKeyLabel->GetPos( x, y );
  563. m_pMoveableSymbolIcon->SetPos( (iMoveWide - m_pMoveableSymbolIcon->GetWide()) * 0.5, y - m_pMoveableSymbolIcon->GetTall() );
  564. m_pMoveableSymbolIcon->GetPos( x, y );
  565. m_pMoveableIcon->SetPos( (iMoveWide - m_pMoveableIcon->GetWide()) * 0.5, y - m_pMoveableIcon->GetTall() );
  566. m_pMoveableIconBG->SetSize( m_pMoveableSubPanel->GetWide(), m_pMoveableSubPanel->GetTall() );
  567. }
  568. }
  569. if ( m_pMoveableSubPanel && m_pMoveableSubPanel->IsVisible() )
  570. {
  571. // Now add our extra width to the total size
  572. iWidth += m_pMoveableSubPanel->GetWide();
  573. }
  574. SetSize( iWidth, GetTall() );
  575. int nOffset = m_bArenaPanelVisible ? YRES (120) : 0; // HACK: move the targetID up a bit so it won't overlap the panel
  576. if( UseVR() )
  577. {
  578. SetPos( ScreenWidth() - iWidth - m_iXOffset, m_nOriginalY - nOffset + YRES( tf_hud_target_id_offset.GetInt() ) );
  579. }
  580. else
  581. {
  582. SetPos( (ScreenWidth() - iWidth) * 0.5, m_nOriginalY - nOffset + YRES( tf_hud_target_id_offset.GetInt() ) );
  583. }
  584. };
  585. //-----------------------------------------------------------------------------
  586. // Purpose:
  587. //-----------------------------------------------------------------------------
  588. int CTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer )
  589. {
  590. int iIndex = pLocalTFPlayer->GetIDTarget();
  591. // If our target entity is already in our secondary ID, don't show it in primary.
  592. CSecondaryTargetID *pSecondaryID = GET_HUDELEMENT( CSecondaryTargetID );
  593. if ( pSecondaryID && pSecondaryID != this && pSecondaryID->GetTargetIndex() == iIndex )
  594. {
  595. iIndex = 0;
  596. }
  597. return iIndex;
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Purpose:
  601. //-----------------------------------------------------------------------------
  602. void CTargetID::UpdateID( void )
  603. {
  604. wchar_t sIDString[ MAX_ID_STRING ] = L"";
  605. wchar_t sDataString[ MAX_ID_STRING ] = L"";
  606. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  607. if ( !pLocalTFPlayer )
  608. return;
  609. // Default the labels' colors
  610. Color colorName = m_LabelColorDefault;
  611. Color colorData = m_LabelColorDefault;
  612. // Get our target's ent index
  613. // Is this an entindex sent by the server?
  614. if ( m_iTargetEntIndex )
  615. {
  616. C_BaseEntity *pEnt = cl_entitylist->GetEnt( m_iTargetEntIndex );
  617. if ( !pEnt )
  618. return;
  619. bool bShowHealth = false;
  620. float flHealth = 0;
  621. float flMaxHealth = 1;
  622. int iMaxBuffedHealth = 0;
  623. int iTargetTeam = pEnt->GetTeamNumber();
  624. const char *pszActionCommand = NULL;
  625. const char *pszActionIcon = NULL;
  626. m_pTargetHealth->SetBuilding( false );
  627. m_pTargetHealth->SetLevel( -1 );
  628. // Some entities we always want to check, cause the text may change
  629. // even while we're looking at it
  630. // Is it a player?
  631. if ( IsPlayerIndex( m_iTargetEntIndex ) )
  632. {
  633. const char *printFormatString = NULL;
  634. wchar_t wszPlayerName[ MAX_PLAYER_NAME_LENGTH ];
  635. bool bDisguisedTarget = false;
  636. bool bDisguisedEnemy = false;
  637. C_TFPlayer *pPlayer = static_cast<C_TFPlayer*>( pEnt );
  638. if ( !pPlayer )
  639. return;
  640. C_TFPlayer *pDisguiseTarget = NULL;
  641. g_pVGuiLocalize->ConvertANSIToUnicode( pPlayer->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );
  642. // determine if the target is a disguised spy (either friendly or enemy)
  643. if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && // they're disguised
  644. //!pPlayer->m_Shared.InCond( TF_COND_DISGUISING ) && // they're not in the process of disguising
  645. !pPlayer->m_Shared.IsStealthed() ) // they're not cloaked
  646. {
  647. bDisguisedTarget = true;
  648. pDisguiseTarget = ToTFPlayer( pPlayer->m_Shared.GetDisguiseTarget() );
  649. if ( pLocalTFPlayer->InSameTeam( pEnt ) == false )
  650. {
  651. iTargetTeam = pPlayer->m_Shared.GetDisguiseTeam();
  652. }
  653. }
  654. if ( bDisguisedTarget )
  655. {
  656. // is the target a disguised enemy spy?
  657. if ( pPlayer->IsEnemyPlayer() )
  658. {
  659. if ( pDisguiseTarget )
  660. {
  661. bDisguisedEnemy = true;
  662. // change the player name
  663. g_pVGuiLocalize->ConvertANSIToUnicode( pDisguiseTarget->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );
  664. // change the team / team color
  665. }
  666. }
  667. }
  668. bool bInSameTeam = pLocalTFPlayer->InSameDisguisedTeam( pEnt );
  669. bool bSpy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_SPY );
  670. bool bMedic = pLocalTFPlayer->IsPlayerClass( TF_CLASS_MEDIC );
  671. bool bHeavy = pLocalTFPlayer->IsPlayerClass( TF_CLASS_HEAVYWEAPONS );
  672. // See if the player wants to fill in the data string
  673. bool bIsAmmoData = false;
  674. bool bIsKillStreakData = false;
  675. pPlayer->GetTargetIDDataString( bDisguisedTarget, sDataString, sizeof(sDataString), bIsAmmoData, bIsKillStreakData );
  676. if ( pLocalTFPlayer->GetTeamNumber() == TEAM_SPECTATOR || bInSameTeam || bSpy || bDisguisedEnemy || bMedic || bHeavy )
  677. {
  678. printFormatString = "#TF_playerid_sameteam";
  679. bShowHealth = true;
  680. }
  681. else if ( pLocalTFPlayer->m_Shared.GetState() == TF_STATE_DYING )
  682. {
  683. // We're looking at an enemy who killed us.
  684. printFormatString = "#TF_playerid_diffteam";
  685. bShowHealth = true;
  686. }
  687. if ( bShowHealth )
  688. {
  689. if ( g_TF_PR )
  690. {
  691. if ( bDisguisedEnemy )
  692. {
  693. flHealth = (float)pPlayer->m_Shared.GetDisguiseHealth();
  694. flMaxHealth = (float)pPlayer->m_Shared.GetDisguiseMaxHealth();
  695. iMaxBuffedHealth = pPlayer->m_Shared.GetDisguiseMaxBuffedHealth();
  696. }
  697. else
  698. {
  699. flHealth = (float)pPlayer->GetHealth();
  700. flMaxHealth = g_TF_PR->GetMaxHealth( m_iTargetEntIndex );
  701. iMaxBuffedHealth = pPlayer->m_Shared.GetMaxBuffedHealth();
  702. }
  703. }
  704. else
  705. {
  706. bShowHealth = false;
  707. }
  708. }
  709. if ( printFormatString )
  710. {
  711. const wchar_t *pszPrepend = GetPrepend();
  712. if ( !pszPrepend || !pszPrepend[0] )
  713. {
  714. pszPrepend = L"";
  715. }
  716. g_pVGuiLocalize->ConstructString_safe( sIDString, g_pVGuiLocalize->Find(printFormatString), 2, pszPrepend, wszPlayerName );
  717. }
  718. // Show target's clip state to attached medics
  719. bool bShowClipInfo = bIsAmmoData &&
  720. sDataString[0] &&
  721. ToTFPlayer( pLocalTFPlayer->MedicGetHealTarget() ) == pPlayer;
  722. if ( m_pTargetAmmoIcon && m_pTargetAmmoIcon->IsVisible() != bShowClipInfo )
  723. {
  724. m_pTargetAmmoIcon->SetVisible( bShowClipInfo );
  725. }
  726. bool bShowKillStreak = bIsKillStreakData && sDataString[0];
  727. if ( m_pTargetKillStreakIcon && m_pTargetKillStreakIcon->IsVisible() != bShowKillStreak )
  728. {
  729. m_pTargetKillStreakIcon->SetVisible( bShowKillStreak );
  730. }
  731. }
  732. else
  733. {
  734. // see if it is an object
  735. if ( pEnt->IsBaseObject() )
  736. {
  737. C_BaseObject *pObj = assert_cast<C_BaseObject *>( pEnt );
  738. pObj->GetTargetIDString( sIDString, sizeof(sIDString), false );
  739. pObj->GetTargetIDDataString( sDataString, sizeof(sDataString) );
  740. bShowHealth = true;
  741. flHealth = pObj->GetHealth();
  742. flMaxHealth = pObj->GetMaxHealth();
  743. m_pTargetHealth->SetBuilding( true );
  744. if ( m_pTargetKillStreakIcon )
  745. {
  746. m_pTargetKillStreakIcon->SetVisible( false );
  747. }
  748. // Switch the icon to the right object
  749. if ( pObj->GetBuilder() == pLocalTFPlayer )
  750. {
  751. int iObj = pObj->GetType();
  752. if ( iObj >= OBJ_DISPENSER && iObj <= OBJ_SENTRYGUN )
  753. {
  754. if ( pLocalTFPlayer->CanPickupBuilding(pObj) )
  755. {
  756. pszActionCommand = "+attack2";
  757. }
  758. switch ( iObj )
  759. {
  760. default:
  761. case OBJ_DISPENSER:
  762. pszActionIcon = "obj_status_dispenser";
  763. break;
  764. case OBJ_TELEPORTER:
  765. {
  766. pszActionIcon = (pObj->GetObjectMode() == MODE_TELEPORTER_ENTRANCE) ? "obj_status_tele_entrance" : "obj_status_tele_exit";
  767. }
  768. break;
  769. case OBJ_SENTRYGUN:
  770. {
  771. int iLevel = pObj->GetUpgradeLevel();
  772. if ( iLevel == 3 )
  773. {
  774. pszActionIcon = "obj_status_sentrygun_3";
  775. }
  776. else
  777. {
  778. pszActionIcon = (iLevel == 2) ? "obj_status_sentrygun_2" : "obj_status_sentrygun_1";
  779. }
  780. }
  781. break;
  782. }
  783. }
  784. }
  785. }
  786. // Generic
  787. else if ( pEnt->IsVisibleToTargetID() )
  788. {
  789. CCaptureFlag *pFlag = dynamic_cast< CCaptureFlag * >( pEnt );
  790. if ( pFlag && pFlag->GetPointValue() > 0 )
  791. {
  792. bShowHealth = false;
  793. g_pVGuiLocalize->ConvertANSIToUnicode( CFmtStr("%d Points", pFlag->GetPointValue() ), sIDString, sizeof(sIDString) );
  794. }
  795. else
  796. {
  797. CTFDroppedWeapon *pDroppedWeapon = dynamic_cast< CTFDroppedWeapon * >( pEnt );
  798. if ( pDroppedWeapon )
  799. {
  800. CEconItemView* pDroppedEconItem = pDroppedWeapon->GetItem();
  801. if ( pLocalTFPlayer->GetDroppedWeaponInRange() != NULL )
  802. {
  803. pszActionIcon = "obj_weapon_pickup";
  804. pszActionCommand = "+use_action_slot_item";
  805. }
  806. if ( FStrEq( pDroppedEconItem->GetStaticData()->GetItemClass(), "tf_weapon_medigun" ) )
  807. {
  808. wchar_t wszChargeLevel[10];
  809. _snwprintf( wszChargeLevel, ARRAYSIZE( wszChargeLevel ) - 1, L"%.0f", pDroppedWeapon->GetChargeLevel() * 100 );
  810. wszChargeLevel[ARRAYSIZE( wszChargeLevel ) - 1] = '\0';
  811. g_pVGuiLocalize->ConstructString_safe( sIDString, L"%s1 (%s2%)", 2, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pDroppedEconItem->GetItemDefinition(), pDroppedEconItem->GetItemQuality() ).GetFullName(), wszChargeLevel );
  812. }
  813. else
  814. {
  815. g_pVGuiLocalize->ConstructString_safe( sIDString, L"%s1", 1, CEconItemLocalizedFullNameGenerator( GLocalizationProvider(), pDroppedEconItem->GetItemDefinition(), pDroppedEconItem->GetItemQuality() ).GetFullName() );
  816. }
  817. locchar_t wszPlayerName [128];
  818. CBasePlayer *pOwner = GetPlayerByAccountID( pDroppedEconItem->GetAccountID() );
  819. // Bots will not work here, so don't fill this out.
  820. if ( pOwner )
  821. {
  822. g_pVGuiLocalize->ConvertANSIToUnicode( pOwner->GetPlayerName(), wszPlayerName, sizeof(wszPlayerName) );
  823. g_pVGuiLocalize->ConstructString_safe( sDataString, g_pVGuiLocalize->Find( "#TF_WhoDropped" ), 1, wszPlayerName );
  824. // Get the rarity color
  825. vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() );
  826. if ( pScheme )
  827. {
  828. const char* pszColorName = GetItemSchema()->GetRarityColor( pDroppedEconItem->GetItemDefinition()->GetRarity() );
  829. pszColorName = pszColorName ? pszColorName : "TanLight";
  830. colorName = pScheme->GetColor( pszColorName, Color( 255, 255, 255, 255 ) );
  831. }
  832. }
  833. }
  834. else if ( pLocalTFPlayer->InSameTeam( pEnt ) )
  835. {
  836. bShowHealth = true;
  837. flHealth = pEnt->GetHealth();
  838. flMaxHealth = pEnt->GetMaxHealth();
  839. iMaxBuffedHealth = pEnt->GetMaxHealth();
  840. // Display respawn timer on revive markers by hacking bountymode's player level display
  841. if ( !pEnt->IsPlayer() )
  842. {
  843. CTFReviveMarker *pMarker = dynamic_cast< CTFReviveMarker* >( pEnt );
  844. if ( pMarker && pMarker->GetOwner() )
  845. {
  846. float flRespawn = TFGameRules()->GetNextRespawnWave( pMarker->GetTeamNumber(), pMarker->GetOwner() ) - gpGlobals->curtime;
  847. m_pTargetHealth->SetLevel( (int)flRespawn );
  848. g_pVGuiLocalize->ConvertANSIToUnicode( pMarker->GetOwner()->GetPlayerName(), sIDString, sizeof(sIDString) );
  849. }
  850. }
  851. }
  852. }
  853. }
  854. }
  855. // Setup health icon
  856. if ( !pEnt->IsAlive() && ( pEnt->IsPlayer() || pEnt->IsBaseObject() ) )
  857. {
  858. flHealth = 0; // fixup for health being 1 when dead
  859. }
  860. m_pTargetHealth->SetHealth( flHealth, flMaxHealth, iMaxBuffedHealth );
  861. m_pTargetHealth->SetVisible( DrawHealthIcon() );
  862. if ( m_pMoveableSubPanel )
  863. {
  864. bool bShowActionKey = pszActionCommand != NULL;
  865. if ( m_pMoveableSubPanel->IsVisible() != bShowActionKey )
  866. {
  867. m_pMoveableSubPanel->SetVisible( bShowActionKey );
  868. m_bLayoutOnUpdate = true;
  869. }
  870. if ( m_pMoveableSubPanel->IsVisible() )
  871. {
  872. const char *pBoundKey = engine->Key_LookupBinding( pszActionCommand );
  873. m_pMoveableSubPanel->SetDialogVariable( "movekey", pBoundKey );
  874. }
  875. if ( m_pMoveableIcon )
  876. {
  877. if ( pszActionIcon )
  878. {
  879. m_pMoveableIcon->SetIcon( pszActionIcon );
  880. }
  881. m_pMoveableIcon->SetVisible( pszActionIcon != NULL );
  882. }
  883. }
  884. if ( m_pAvatarImage && pEnt->IsPlayer() )
  885. {
  886. C_BasePlayer *pTFTarget = GetTargetForSteamAvatar( ToTFPlayer( pEnt ) );
  887. bool bShowAvatar = ( pTFTarget ) ? true : false;
  888. if ( m_pAvatarImage->IsVisible() != bShowAvatar )
  889. {
  890. m_pAvatarImage->SetVisible( bShowAvatar );
  891. if ( bShowAvatar )
  892. {
  893. m_pAvatarImage->SetPlayer( pTFTarget );
  894. m_pAvatarImage->SetShouldDrawFriendIcon( false );
  895. m_pAvatarImage->SetAlpha( tf_hud_target_id_alpha.GetInt() );
  896. }
  897. }
  898. }
  899. if ( m_pTargetNameLabel && m_pTargetDataLabel )
  900. {
  901. int iNameW, iDataW, iIgnored;
  902. m_pTargetNameLabel->GetContentSize( iNameW, iIgnored );
  903. m_pTargetDataLabel->GetContentSize( iDataW, iIgnored );
  904. // Target name
  905. if ( sIDString[0] )
  906. {
  907. sIDString[ ARRAYSIZE(sIDString)-1 ] = '\0';
  908. m_pTargetNameLabel->SetVisible(true);
  909. m_pTargetNameLabel->SetFgColor( colorName );
  910. // TODO: Support if( hud_centerid.GetInt() == 0 )
  911. SetDialogVariable( "targetname", sIDString );
  912. }
  913. else
  914. {
  915. m_pTargetNameLabel->SetVisible(false);
  916. m_pTargetNameLabel->SetText("");
  917. }
  918. // Extra target data
  919. if ( sDataString[0] )
  920. {
  921. sDataString[ ARRAYSIZE(sDataString)-1 ] = '\0';
  922. m_pTargetDataLabel->SetVisible(true);
  923. m_pTargetDataLabel->SetFgColor( colorData );
  924. SetDialogVariable( "targetdata", sDataString );
  925. }
  926. else
  927. {
  928. m_pTargetDataLabel->SetVisible(false);
  929. m_pTargetDataLabel->SetText("");
  930. }
  931. int iPostNameW, iPostDataW;
  932. m_pTargetNameLabel->GetContentSize( iPostNameW, iIgnored );
  933. m_pTargetDataLabel->GetContentSize( iPostDataW, iIgnored );
  934. if ( m_pBGPanel )
  935. {
  936. m_pBGPanel->SetBGTeam( iTargetTeam );
  937. m_pBGPanel->UpdateBGImage();
  938. m_pBGPanel->SetAlpha( tf_hud_target_id_alpha.GetInt() );
  939. }
  940. if ( m_bLayoutOnUpdate || (iPostDataW != iDataW) || (iPostNameW != iNameW) )
  941. {
  942. InvalidateLayout( true );
  943. m_bLayoutOnUpdate = false;
  944. }
  945. }
  946. }
  947. }
  948. //-----------------------------------------------------------------------------
  949. // Purpose:
  950. //-----------------------------------------------------------------------------
  951. CSecondaryTargetID::CSecondaryTargetID( const char *pElementName ) : CTargetID( pElementName )
  952. {
  953. m_wszPrepend[0] = '\0';
  954. RegisterForRenderGroup( "mid" );
  955. m_bWasHidingLowerElements = false;
  956. }
  957. //-----------------------------------------------------------------------------
  958. // Purpose:
  959. //-----------------------------------------------------------------------------
  960. bool CSecondaryTargetID::ShouldDraw( void )
  961. {
  962. bool bDraw = BaseClass::ShouldDraw();
  963. if ( bDraw )
  964. {
  965. if ( !m_bWasHidingLowerElements )
  966. {
  967. HideLowerPriorityHudElementsInGroup( "mid" );
  968. m_bWasHidingLowerElements = true;
  969. }
  970. }
  971. else
  972. {
  973. if ( m_bWasHidingLowerElements )
  974. {
  975. UnhideLowerPriorityHudElementsInGroup( "mid" );
  976. m_bWasHidingLowerElements = false;
  977. }
  978. }
  979. return bDraw;
  980. }
  981. //-----------------------------------------------------------------------------
  982. // Purpose:
  983. //-----------------------------------------------------------------------------
  984. int CSecondaryTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer )
  985. {
  986. // If we're a medic & we're healing someone, target him.
  987. CBaseEntity *pHealTarget = pLocalTFPlayer->MedicGetHealTarget();
  988. if ( pHealTarget )
  989. {
  990. if ( pHealTarget->entindex() != m_iTargetEntIndex )
  991. {
  992. g_pVGuiLocalize->ConstructString_safe( m_wszPrepend, g_pVGuiLocalize->Find("#TF_playerid_healtarget" ), 0 );
  993. }
  994. return pHealTarget->entindex();
  995. }
  996. // If we have a healer, target him.
  997. C_TFPlayer *pHealer;
  998. float flHealerChargeLevel;
  999. pLocalTFPlayer->GetHealer( &pHealer, &flHealerChargeLevel );
  1000. if ( pHealer )
  1001. {
  1002. if ( pHealer->entindex() != m_iTargetEntIndex )
  1003. {
  1004. g_pVGuiLocalize->ConstructString_safe( m_wszPrepend, g_pVGuiLocalize->Find("#TF_playerid_healer" ), 0 );
  1005. }
  1006. return pHealer->entindex();
  1007. }
  1008. if ( m_iTargetEntIndex )
  1009. {
  1010. m_wszPrepend[0] = '\0';
  1011. }
  1012. return 0;
  1013. }
  1014. // Separately declared versions of the hud element for alive and dead so they
  1015. // can have different positions
  1016. bool CMainTargetID::ShouldDraw( void )
  1017. {
  1018. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  1019. if ( !pLocalTFPlayer )
  1020. return false;
  1021. if ( pLocalTFPlayer->GetObserverMode() > OBS_MODE_NONE )
  1022. return false;
  1023. return BaseClass::ShouldDraw();
  1024. }
  1025. bool CSpectatorTargetID::ShouldDraw( void )
  1026. {
  1027. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  1028. if ( !pLocalTFPlayer )
  1029. return false;
  1030. if ( pLocalTFPlayer->GetObserverMode() <= OBS_MODE_NONE ||
  1031. pLocalTFPlayer->GetObserverMode() == OBS_MODE_FREEZECAM )
  1032. return false;
  1033. if ( pLocalTFPlayer->m_bIsCoaching )
  1034. {
  1035. return false;
  1036. }
  1037. // Hide panel for freeze-cam screenshot?
  1038. extern bool IsTakingAFreezecamScreenshot();
  1039. extern ConVar hud_freezecamhide;
  1040. if ( IsTakingAFreezecamScreenshot() && hud_freezecamhide.GetBool() )
  1041. return false;
  1042. #if defined( REPLAY_ENABLED )
  1043. if ( g_pEngineClientReplay->IsPlayingReplayDemo() )
  1044. return false;
  1045. #endif
  1046. return BaseClass::ShouldDraw();
  1047. }
  1048. int CSpectatorTargetID::CalculateTargetIndex( C_TFPlayer *pLocalTFPlayer )
  1049. {
  1050. int iIndex = BaseClass::CalculateTargetIndex( pLocalTFPlayer );
  1051. #if defined( REPLAY_ENABLED )
  1052. // Don't execute this if we're watching a replay
  1053. if ( ( !g_pEngineClientReplay || !g_pEngineClientReplay->IsPlayingReplayDemo() ) && pLocalTFPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalTFPlayer->GetObserverTarget() )
  1054. #else
  1055. if ( pLocalTFPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalTFPlayer->GetObserverTarget() )
  1056. #endif
  1057. {
  1058. iIndex = pLocalTFPlayer->GetObserverTarget()->entindex();
  1059. }
  1060. return iIndex;
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose:
  1064. //-----------------------------------------------------------------------------
  1065. void CSpectatorTargetID::ApplySchemeSettings( vgui::IScheme *scheme )
  1066. {
  1067. BaseClass::ApplySchemeSettings( scheme );
  1068. if ( m_pBGPanel )
  1069. {
  1070. m_pBGPanel->SetVisible( false );
  1071. }
  1072. m_pBGPanel_Spec_Blue = FindChildByName("TargetIDBG_Spec_Blue");
  1073. m_pBGPanel_Spec_Red = FindChildByName("TargetIDBG_Spec_Red");
  1074. if ( m_pBGPanel_Spec_Blue )
  1075. {
  1076. m_pBGPanel_Spec_Blue->SetVisible( true );
  1077. }
  1078. }
  1079. //-----------------------------------------------------------------------------
  1080. // Purpose:
  1081. //-----------------------------------------------------------------------------
  1082. void CSpectatorTargetID::PerformLayout( void )
  1083. {
  1084. int iXIndent = XRES(5);
  1085. int iXPostdent = XRES(10);
  1086. int iWidth = m_pTargetHealth->GetWide() + iXIndent + iXPostdent;
  1087. int iTextW, iTextH;
  1088. int iDataW, iDataH;
  1089. if ( m_pTargetNameLabel && m_pTargetDataLabel )
  1090. {
  1091. m_pTargetNameLabel->GetContentSize( iTextW, iTextH );
  1092. m_pTargetDataLabel->GetContentSize( iDataW, iDataH );
  1093. iWidth += MAX(iTextW,iDataW);
  1094. SetSize( iWidth, GetTall() );
  1095. int nOffset = m_bArenaPanelVisible ? YRES (120) : 0; // HACK: move the targetID up a bit so it won't overlap the panel
  1096. int x1 = 0, y1 = 0;
  1097. int x2 = 0, y2 = 0;
  1098. int x3 = 0, y3 = 0;
  1099. m_pTargetNameLabel->GetPos( x1, y1 );
  1100. m_pTargetDataLabel->GetPos( x2, y2 );
  1101. if ( m_pTargetKillStreakIcon )
  1102. {
  1103. m_pTargetKillStreakIcon->GetPos( x3, y3 );
  1104. }
  1105. // Shift Labels
  1106. {
  1107. int nBuffer = ( m_pAvatarImage && m_pAvatarImage->IsVisible() ) ? 6 : 8;
  1108. m_pTargetNameLabel->SetPos( XRES( nBuffer ) + m_pTargetHealth->GetWide(), y1 );
  1109. m_pTargetDataLabel->SetPos( XRES( nBuffer ) + m_pTargetHealth->GetWide(), y2 );
  1110. if ( m_pTargetKillStreakIcon )
  1111. {
  1112. m_pTargetKillStreakIcon->SetPos( XRES( 10 ) + m_pTargetHealth->GetWide(), y3 );
  1113. }
  1114. }
  1115. if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_NORMAL )
  1116. {
  1117. SetPos( (ScreenWidth() - iWidth) * 0.5, m_nOriginalY - nOffset );
  1118. }
  1119. else
  1120. {
  1121. int iBottomBarHeight = 0;
  1122. if ( g_pSpectatorGUI && g_pSpectatorGUI->IsVisible() )
  1123. {
  1124. iBottomBarHeight = g_pSpectatorGUI->GetBottomBarHeight();
  1125. }
  1126. int iYPos = ScreenHeight() - GetTall() - iBottomBarHeight - m_iYOffset;
  1127. if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_LEFT )
  1128. {
  1129. SetPos( m_iXOffset, iYPos );
  1130. }
  1131. else if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_CENTER )
  1132. {
  1133. SetPos( (ScreenWidth() - iWidth) * 0.5, iYPos );
  1134. }
  1135. else if ( tf_spectator_target_location.GetInt() == SPECTATOR_TARGET_ID_BOTTOM_RIGHT )
  1136. {
  1137. SetPos( ScreenWidth() - iWidth - m_iXOffset, iYPos );
  1138. }
  1139. }
  1140. if ( m_pBGPanel_Spec_Blue )
  1141. {
  1142. m_pBGPanel_Spec_Blue->SetSize( iWidth, GetTall() );
  1143. }
  1144. if ( m_pBGPanel_Spec_Red )
  1145. {
  1146. m_pBGPanel_Spec_Red->SetSize( iWidth, GetTall() );
  1147. }
  1148. if ( m_pBGPanel_Spec_Blue && m_pBGPanel_Spec_Red )
  1149. {
  1150. if ( m_iTargetEntIndex )
  1151. {
  1152. C_BaseEntity *pEnt = cl_entitylist->GetEnt( m_iTargetEntIndex );
  1153. if ( pEnt )
  1154. {
  1155. bool bRed = ( pEnt->GetTeamNumber() == TF_TEAM_RED );
  1156. m_pBGPanel_Spec_Blue->SetVisible( !bRed );
  1157. m_pBGPanel_Spec_Red->SetVisible( bRed );
  1158. m_pBGPanel_Spec_Blue->SetAlpha( tf_hud_target_id_alpha.GetInt() );
  1159. m_pBGPanel_Spec_Red->SetAlpha( tf_hud_target_id_alpha.GetInt() );
  1160. }
  1161. }
  1162. }
  1163. }
  1164. }
  1165. //-----------------------------------------------------------------------------
  1166. // Purpose:
  1167. //-----------------------------------------------------------------------------
  1168. CFloatingHealthIcon::CFloatingHealthIcon( vgui::Panel *parent, const char *name ) : EditablePanel( parent, name )
  1169. {
  1170. m_flPrevHealth = -1.f;
  1171. m_nPrevLevel = 0;
  1172. SetVisible( false );
  1173. SetBounds( 0, 0, 128, 128 );
  1174. vgui::ivgui()->AddTickSignal( GetVPanel(), 50 );
  1175. OnTick();
  1176. m_pTargetHealth = new CTFSpectatorGUIHealth( this, "SpectatorGUIHealth" );
  1177. }
  1178. //-----------------------------------------------------------------------------
  1179. // Purpose:
  1180. //-----------------------------------------------------------------------------
  1181. void CFloatingHealthIcon::Reset( void )
  1182. {
  1183. m_pTargetHealth->Reset();
  1184. }
  1185. //-----------------------------------------------------------------------------
  1186. // Purpose:
  1187. //-----------------------------------------------------------------------------
  1188. void CFloatingHealthIcon::SetEntity( C_BaseEntity *pEntity )
  1189. {
  1190. m_hEntity = pEntity;
  1191. if ( !m_pTargetHealth )
  1192. return;
  1193. m_pTargetHealth->SetAllowAnimations( false );
  1194. m_pTargetHealth->HideHealthBonusImage();
  1195. bool bBuilding = false;
  1196. if ( m_hEntity->IsPlayer() )
  1197. {
  1198. C_TFPlayer *pPlayer = ToTFPlayer( m_hEntity );
  1199. bBuilding = ( pPlayer && pPlayer->IsMiniBoss() ) ? true : false;
  1200. }
  1201. m_pTargetHealth->SetBuilding( bBuilding );
  1202. }
  1203. //-----------------------------------------------------------------------------
  1204. // Purpose:
  1205. //-----------------------------------------------------------------------------
  1206. CFloatingHealthIcon* CFloatingHealthIcon::AddFloatingHealthIcon( C_BaseEntity *pEntity )
  1207. {
  1208. CFloatingHealthIcon *pHealthIcon = new CFloatingHealthIcon( g_pClientMode->GetViewport(), "HealthIcon" );
  1209. vgui::SETUP_PANEL( pHealthIcon );
  1210. pHealthIcon->SetEntity( pEntity );
  1211. return pHealthIcon;
  1212. }
  1213. //-----------------------------------------------------------------------------
  1214. // Purpose:
  1215. //-----------------------------------------------------------------------------
  1216. void CFloatingHealthIcon::ApplySchemeSettings( vgui::IScheme *pScheme )
  1217. {
  1218. BaseClass::ApplySchemeSettings( pScheme );
  1219. LoadControlSettings( "resource/UI/HealthIconPanel.res" );
  1220. SetVisible( false );
  1221. }
  1222. //-----------------------------------------------------------------------------
  1223. // Purpose:
  1224. //-----------------------------------------------------------------------------
  1225. void CFloatingHealthIcon::OnTick( void )
  1226. {
  1227. if ( !m_pTargetHealth )
  1228. return;
  1229. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  1230. if ( !ShouldHealthBarBeVisible( m_hEntity, pLocalTFPlayer ) )
  1231. {
  1232. SetVisible( false );
  1233. return;
  1234. }
  1235. C_TFPlayer *pTargetPlayer = ToTFPlayer( m_hEntity );
  1236. if ( pTargetPlayer && pTargetPlayer->m_Shared.IsStealthed() )
  1237. {
  1238. SetVisible( false );
  1239. return;
  1240. }
  1241. // Defaults for all entities
  1242. float flHealth = m_hEntity->GetHealth();
  1243. float flMaxHealth = m_hEntity->GetMaxHealth();
  1244. float iMaxBuffedHealth = m_hEntity->GetMaxHealth();
  1245. if ( pTargetPlayer && pTargetPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTargetPlayer->IsEnemyPlayer() )
  1246. {
  1247. flHealth = (float)pTargetPlayer->m_Shared.GetDisguiseHealth();
  1248. flMaxHealth = (float)pTargetPlayer->m_Shared.GetDisguiseMaxHealth();
  1249. iMaxBuffedHealth = pTargetPlayer->m_Shared.GetDisguiseMaxBuffedHealth();
  1250. }
  1251. if ( flHealth != m_flPrevHealth )
  1252. {
  1253. m_pTargetHealth->SetHealth( flHealth, flMaxHealth, iMaxBuffedHealth );
  1254. m_flPrevHealth = flHealth;
  1255. }
  1256. #ifdef STAGING_ONLY
  1257. if ( TFGameRules() && TFGameRules()->IsBountyMode() && tf_bountymode_showhealth.GetInt() == 2 )
  1258. {
  1259. if ( m_hEntity->IsPlayer() )
  1260. {
  1261. if ( !pTargetPlayer || pTargetPlayer->IsMiniBoss() )
  1262. return;
  1263. int nPlayerLevel = pTargetPlayer->GetExperienceLevel();
  1264. if ( nPlayerLevel != m_nPrevLevel )
  1265. {
  1266. m_pTargetHealth->SetLevel( nPlayerLevel );
  1267. m_nPrevLevel = nPlayerLevel;
  1268. }
  1269. }
  1270. }
  1271. #endif // STAGING_ONLY
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Purpose:
  1275. //-----------------------------------------------------------------------------
  1276. ConVar tf_healthicon_height_offset( "tf_healthicon_height_offset", "10", FCVAR_ARCHIVE, "Offset of the health icon away from the top of the target." );
  1277. void CFloatingHealthIcon::Paint( void )
  1278. {
  1279. if ( !CalculatePosition() )
  1280. return;
  1281. BaseClass::Paint();
  1282. }
  1283. //-----------------------------------------------------------------------------
  1284. bool CFloatingHealthIcon::CalculatePosition( )
  1285. {
  1286. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  1287. if ( !pLocalTFPlayer )
  1288. return false;
  1289. if ( !m_hEntity || m_hEntity->IsDormant() )
  1290. {
  1291. return false;
  1292. }
  1293. Vector vecTarget = m_hEntity->GetAbsOrigin();
  1294. // Reposition based on our target's position
  1295. Vector vecDistance = vecTarget - pLocalTFPlayer->GetAbsOrigin();
  1296. vecTarget.z += VEC_HULL_MAX_SCALED( m_hEntity->GetBaseAnimating() ).z + tf_healthicon_height_offset.GetInt() + m_hEntity->GetHealthBarHeightOffset();
  1297. int iX, iY;
  1298. GetVectorInHudSpace( vecTarget, iX, iY ); // TODO: GetVectorInHudSpace or GetVectorInScreenSpace?
  1299. SetPos( iX - ( GetWide() / 2 ), iY - GetTall() );
  1300. return true;
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. void CFloatingHealthIcon::SetVisible( bool state )
  1304. {
  1305. if ( state )
  1306. {
  1307. CalculatePosition();
  1308. }
  1309. BaseClass::SetVisible( state );
  1310. }
  1311. //-----------------------------------------------------------------------------
  1312. bool CFloatingHealthIcon::IsVisible( void )
  1313. {
  1314. if ( !m_pTargetHealth )
  1315. return false;
  1316. C_TFPlayer *pLocalTFPlayer = C_TFPlayer::GetLocalTFPlayer();
  1317. if ( !pLocalTFPlayer )
  1318. return false;
  1319. //if ( pLocalTFPlayer->GetObserverMode() == OBS_MODE_FREEZECAM )
  1320. if ( pLocalTFPlayer->GetObserverMode() > OBS_MODE_NONE )
  1321. return false;
  1322. if ( TFGameRules() && TFGameRules()->ShowMatchSummary() )
  1323. return false;
  1324. return BaseClass::IsVisible();
  1325. }