//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "iclientmode.h" #include #include #include #include "tf_gamerules.h" #include "tf_logic_halloween_2014.h" #include "c_tf_playerresource.h" #include "tf_playerpanel.h" #include "tf_teamstatus.h" #include "tf_matchmaking_shared.h" #include "tf_match_description.h" using namespace vgui; // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" CTFTeamStatusPlayerPanel::CTFTeamStatusPlayerPanel( vgui::Panel *parent, const char *name ) : CTFPlayerPanel( parent, name ) { m_pHealthBar = new vgui::ContinuousProgressBar( this, "healthbar" ); m_pOverhealBar = new vgui::ContinuousProgressBar( this, "overhealbar" ); m_pClassImageBG = new vgui::Panel( this, "classimagebg" ); m_pDeathFlag = new vgui::ImagePanel( this, "DeathPanel" ); m_iTeam = TEAM_UNASSIGNED; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatusPlayerPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatusPlayerPanel::UpdateBorder( void ) { // do nothing } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFTeamStatusPlayerPanel::Update( void ) { if ( !g_TF_PR || !TFGameRules() ) return false; C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); if ( !pLocalPlayer || ( pLocalPlayer->GetTeamNumber() < FIRST_GAME_TEAM ) ) return false; bool bChanged = false; bool bVisible = GetTeam() >= FIRST_GAME_TEAM; int iRespawnWait = -1; if ( IsVisible() != bVisible ) { SetVisible( bVisible ); bChanged = true; } if ( bVisible && g_TF_PR ) { bool bSameTeamAsLocalPlayer = ( GetTeam() == g_TF_PR->GetTeam( pLocalPlayer->entindex() ) ); // are we connected with a class? Assert( TF_CLASS_UNDEFINED == 0 ); int iClass = -1; bool bAlive = false; int iHealth = -1; bool bIsLocalPlayer = false; if ( m_iPlayerIndex > 0 ) { bIsLocalPlayer = pLocalPlayer->entindex() == m_iPlayerIndex; iClass = g_TF_PR->GetPlayerClass( m_iPlayerIndex ); if ( iClass != TF_CLASS_UNDEFINED ) { bAlive = g_TF_PR->IsAlive( m_iPlayerIndex ); } if ( bAlive ) { iHealth = g_TF_PR->GetHealth( m_iPlayerIndex ); } // calc respawn time remaining if ( !bAlive && ( iClass != TF_CLASS_UNDEFINED ) ) { float flRespawnAt = g_TF_PR->GetNextRespawnTime( m_iPlayerIndex ); iRespawnWait = ( flRespawnAt - gpGlobals->curtime ); if ( iRespawnWait <= 0 ) iRespawnWait = -1; } // hide class info from the other team? if ( !bSameTeamAsLocalPlayer ) { iClass = TF_CLASS_UNDEFINED; } } if ( m_iTeam != GetTeam() ) { m_iTeam = GetTeam(); bChanged = true; } if ( m_pClassImageBG ) { if ( m_iTeam == TF_TEAM_RED ) { Color bgColor( m_ColorPortraitBGRedLocalPlayer ); if ( !bIsLocalPlayer ) { bgColor = bAlive ? m_ColorPortraitBGRed : m_ColorPortraitBGRedDead; } m_pClassImageBG->SetBgColor( bgColor ); } else { Color bgColor( m_ColorPortraitBGBlueLocalPlayer ); if ( !bIsLocalPlayer ) { bgColor = bAlive ? m_ColorPortraitBGBlue : m_ColorPortraitBGBlueDead; } m_pClassImageBG->SetBgColor( bgColor ); } } // update live state if ( m_pClassImage ) { if ( m_bPrevAlive != bAlive ) { bChanged = true; m_bPrevAlive = bAlive; if ( !bAlive ) { m_pClassImage->SetDrawColor( ( m_iTeam == TF_TEAM_RED ) ? m_ColorPortraitBlendDeadRed : m_ColorPortraitBlendDeadBlue ); } m_pDeathFlag->SetImage( ( m_iTeam == TF_TEAM_RED ) ? "../HUD/comp_player_status" : "../HUD/comp_player_status_blue" ); if ( bAlive ) { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerAlive", false ); } else { g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( this, "TeamStatus_PlayerDead", false ); } } // update class image if ( ( m_iPrevClass != iClass ) || bChanged ) { bChanged = true; m_iPrevClass = iClass; if ( iClass < 0 ) { m_pClassImage->SetImage( "hud_connecting" ); } else if ( iClass == TF_CLASS_UNDEFINED ) { int iDeadClass = g_TF_PR->GetPlayerClassWhenKilled( m_iPlayerIndex ); if ( !bAlive && !bSameTeamAsLocalPlayer && ( m_iTeam >= FIRST_GAME_TEAM ) && ( iDeadClass > TF_CLASS_UNDEFINED ) ) { m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iDeadClass + 9] : g_pszItemClassImagesBlue[iDeadClass + 9] ) ); } else { m_pClassImage->SetImage( "class_portraits/silhouette_alpha" ); } } else { if ( bAlive ) { m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass] : g_pszItemClassImagesBlue[iClass] ) ); } else { m_pClassImage->SetImage( VarArgs( "%s_alpha", ( m_iTeam == TF_TEAM_RED ) ? g_pszItemClassImagesRed[iClass + 9] : g_pszItemClassImagesBlue[iClass + 9] ) ); } } } } // update health indicator if ( m_pHealthBar && m_pOverhealBar ) { if ( iHealth != m_iPrevHealth ) { m_iPrevHealth = iHealth; if ( ( iHealth > 0 ) && bSameTeamAsLocalPlayer ) { float flMaxHealth = g_TF_PR->GetMaxHealth( m_iPlayerIndex ); float flProgress = iHealth / flMaxHealth; // health bar if ( !m_pHealthBar->IsVisible() ) { m_pHealthBar->SetVisible( true ); } m_pHealthBar->SetProgress( ( flProgress >= 1.0f ) ? 1.0f : flProgress ); if ( flProgress > m_flPercentageHealthMed ) { m_pHealthBar->SetFgColor( m_ColorBarHealthHigh ); } else if ( flProgress > m_flPercentageHealthLow ) { m_pHealthBar->SetFgColor( m_ColorBarHealthMed ); } else { m_pHealthBar->SetFgColor( m_ColorBarHealthLow ); } // overheal bar if ( flProgress > 1.0f ) { if ( !m_pOverhealBar->IsVisible() ) { m_pOverhealBar->SetVisible( true ); } m_pOverhealBar->SetProgress( flProgress - 1.0f ); } else { if ( m_pOverhealBar->IsVisible() ) { m_pOverhealBar->SetVisible( false ); } } } else { if ( m_pHealthBar->IsVisible() ) { m_pHealthBar->SetVisible( false ); } if ( m_pOverhealBar->IsVisible() ) { m_pOverhealBar->SetVisible( false ); } } bChanged = true; } } // update respawn time if ( iRespawnWait != m_iPrevRespawnWait ) { m_iPrevRespawnWait = iRespawnWait; if ( ( iRespawnWait < 0 ) || !bSameTeamAsLocalPlayer ) { SetDialogVariable( "respawntime", "" ); } else { SetDialogVariable( "respawntime", VarArgs( "%d", iRespawnWait ) ); } bChanged = true; } // gamerules state if ( TFGameRules()->State_Get() != m_iPrevState ) { m_iPrevState = TFGameRules()->State_Get(); bChanged = true; } } return bChanged; } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CTFTeamStatus::CTFTeamStatus( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) { m_pPlayerPanelKVs = NULL; m_bReapplyPlayerPanelKVs = false; Reset(); ivgui()->AddTickSignal( GetVPanel(), 100 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFTeamStatus::~CTFTeamStatus() { if ( m_pPlayerPanelKVs ) { m_pPlayerPanelKVs->deleteThis(); m_pPlayerPanelKVs = NULL; } } void SetGrowDir( CTFTeamStatus::EGrowDir* pGrowDir, const char* pszString ) { if ( FStrEq( pszString, "east" ) ) { (*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_EAST; } else if ( FStrEq( pszString, "west" ) ) { (*pGrowDir) = CTFTeamStatus::EGrowDir::GROW_WEST; } else { Assert( !"Invalid direction string for team status team grow direction" ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); SetGrowDir( &m_eTeam1GrowDir, inResourceData->GetString( "team1_grow_dir" ) ); SetGrowDir( &m_eTeam2GrowDir, inResourceData->GetString( "team2_grow_dir" ) ); KeyValues *pItemKV = inResourceData->FindKey( "playerpanels_kv" ); if ( pItemKV ) { if ( m_pPlayerPanelKVs ) { m_pPlayerPanelKVs->deleteThis(); } m_pPlayerPanelKVs = new KeyValues( "playerpanels_kv" ); pItemKV->CopySubkeys( m_pPlayerPanelKVs ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); m_bReapplyPlayerPanelKVs = true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::PerformLayout( void ) { BaseClass::PerformLayout(); int iTeam1Count = 0; int iTeam2Count = 0; // Tally up how many are on each team so we can scale them appropriately down below for ( int i = 0; i < m_PlayerPanels.Count(); i++ ) { if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_BLUE ) { ++iTeam1Count; } else if ( m_PlayerPanels[i]->GetTeam() == TF_TEAM_RED ) { ++iTeam2Count; } } C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer(); // Snag local player info int nLocalPlayerTeam = TF_TEAM_COUNT; int nLocalPlayerIndex = -1; if ( pLocalPlayer ) { nLocalPlayerIndex = pLocalPlayer->entindex(); nLocalPlayerTeam = pLocalPlayer->GetTeamNumber(); } int iTeam1Processed = 0; int iTeam2Processed = 0; for ( int i = 0; i < m_PlayerPanels.Count(); i++ ) { if ( m_PlayerPanels[i]->GetPlayerIndex() <= 0 ) { m_PlayerPanels[i]->SetVisible( false ); continue; } bool bIsLocalPlayerPanel = nLocalPlayerIndex == m_PlayerPanels[i]->GetPlayerIndex(); int iTeam = m_PlayerPanels[i]->GetTeam(); int iXPos = 0; // Setup vars EGrowDir& eGrowDir = iTeam == TF_TEAM_BLUE ? m_eTeam1GrowDir : m_eTeam2GrowDir; int& iTeamCount = iTeam == TF_TEAM_BLUE ? iTeam1Count : iTeam2Count; int& iBaseX = iTeam == TF_TEAM_BLUE ? m_iTeam1BaseX : m_iTeam2BaseX; int& iProcessed = iTeam == TF_TEAM_BLUE ? iTeam1Processed : iTeam2Processed; int& iMaxExpand = iTeam == TF_TEAM_BLUE ? m_iTeam1MaxExpand : m_iTeam2MaxExpand; const int iGap = RemapValClamped( iTeamCount, 6, 12, m_i6v6Gap, m_i12v12Gap ); // Local player is always the innermost panel int nTeamPanelIndex = bIsLocalPlayerPanel ? 0 : iTeam == nLocalPlayerTeam ? iProcessed + 1 : iProcessed; // Setup X-position and widths // Use the max width if less than 6 (to fill out the space) int nNewWide = iTeamCount <= 6 ? m_iMaxSize : ( iMaxExpand - ( iGap * ( iTeamCount - 1 ) ) ) / iTeamCount; // How far each panel is apart from each other const int iXStep = iGap + nNewWide; // How many steps out this panel should be const int iXOffset = iXStep * nTeamPanelIndex; // Step out the panels by the step iXPos = eGrowDir == GROW_EAST ? iBaseX + iXOffset // Align the left edge of the left-most panel on the specified point : eGrowDir == GROW_WEST ? iBaseX - iXOffset - nNewWide // Align the right edge of the right-most panel on the specified point : 0; // This is expensive, so only do it if we need to if ( nNewWide != m_PlayerPanels[i]->GetWide() ) { // We change the width in the KV's then reapply settings so the children will do their // magical proportionaltoparent layout. if ( m_pPlayerPanelKVs ) { m_pPlayerPanelKVs->SetInt( "wide", scheme()->GetProportionalNormalizedValueEx( GetScheme(), nNewWide ) ); m_PlayerPanels[i]->ApplySettings( m_pPlayerPanelKVs ); m_PlayerPanels[i]->InvalidateLayout( false, true ); m_PlayerPanels[i]->Update(); } } if ( !bIsLocalPlayerPanel ) { ++iProcessed; } m_PlayerPanels[i]->SetPos( iXPos, m_PlayerPanels[i]->GetYPos() ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::Reset() { for ( int i = 0; i < m_PlayerPanels.Count(); i++ ) { m_PlayerPanels[i]->Reset(); } InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CTFTeamStatus::ShouldDraw( void ) { // Get the player and active weapon. C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer(); if ( !pPlayer ) return false; int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() ); if ( iLocalTeam < FIRST_GAME_TEAM ) return false; if ( TFGameRules() ) { const IMatchGroupDescription* pMatchDesc = GetMatchGroupDescription( TFGameRules()->GetCurrentMatchGroup() ); if ( !pMatchDesc || !pMatchDesc->m_params.m_bUseMatchHud ) return false; if ( TFGameRules()->ShowMatchSummary() ) return false; } return true; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::OnTick() { if ( IsVisible() != ShouldDraw() ) { SetVisible( ShouldDraw() ); } if ( IsVisible() ) { RecalculatePlayerPanels(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CTFTeamStatusPlayerPanel *CTFTeamStatus::GetOrAddPanel( int iPanelIndex ) { if ( iPanelIndex < m_PlayerPanels.Count() ) { return m_PlayerPanels[iPanelIndex]; } Assert( iPanelIndex == m_PlayerPanels.Count() ); CTFTeamStatusPlayerPanel *pPanel = new CTFTeamStatusPlayerPanel( this, VarArgs( "playerpanel%d", iPanelIndex ) ); if ( m_pPlayerPanelKVs ) { pPanel->ApplySettings( m_pPlayerPanelKVs ); pPanel->InvalidateLayout( false, true ); InvalidateLayout(); } m_PlayerPanels.AddToTail( pPanel ); return pPanel; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::RecalculatePlayerPanels( void ) { C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( !pPlayer || !g_TF_PR ) return; int iPanel = 0; bool bNeedsLayout = false; int iLocalTeam = g_TF_PR->GetTeam( pPlayer->entindex() ); if ( iLocalTeam >= FIRST_GAME_TEAM ) { for ( int i = 1; i <= MAX_PLAYERS; i++ ) { if ( !g_TF_PR->IsConnected( i ) ) continue; int iTeam = g_TF_PR->GetTeam( i ); if ( iTeam < FIRST_GAME_TEAM ) continue; // Add an entry CTFTeamStatusPlayerPanel *pPanel = GetOrAddPanel( iPanel ); if ( pPanel->GetPlayerIndex() != i ) { bNeedsLayout = true; } pPanel->SetPlayerIndex( i ); if ( pPanel->GetPreviousTeam() != pPanel->GetTeam() ) { bNeedsLayout = true; } ++iPanel; } } // Clear out any extra panels for ( int i = iPanel; i < m_PlayerPanels.Count(); i++ ) { if ( m_PlayerPanels[i]->GetPlayerIndex() != 0 ) { bNeedsLayout = true; } m_PlayerPanels[i]->SetPlayerIndex( 0 ); } UpdatePlayerPanels(); if ( bNeedsLayout ) { InvalidateLayout(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFTeamStatus::UpdatePlayerPanels( void ) { if ( !g_TF_PR ) return; for ( int i = 0; i < m_PlayerPanels.Count(); i++ ) { m_PlayerPanels[i]->Update(); } }