summaryrefslogtreecommitdiff
path: root/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp')
-rw-r--r--game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp1596
1 files changed, 1596 insertions, 0 deletions
diff --git a/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp b/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp
new file mode 100644
index 0000000..eb2a652
--- /dev/null
+++ b/game/client/cstrike/VGUI/cstrikeclientscoreboard.cpp
@@ -0,0 +1,1596 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "hud.h"
+#include "cstrikeclientscoreboard.h"
+#include "c_team.h"
+#include "c_cs_playerresource.h"
+#include "c_cs_player.h"
+#include "cs_gamerules.h"
+#include "backgroundpanel.h"
+#include "clientmode.h"
+
+#include <KeyValues.h>
+
+#include <vgui/IScheme.h>
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vgui_controls/SectionedListPanel.h>
+#include <vgui_controls/ScalableImagePanel.h>
+#include "VGuiMatSurface/IMatSystemSurface.h"
+
+#include "voice_status.h"
+#include "vgui_avatarimage.h"
+
+#include "achievementmgr.h"
+#include "engine/imatchmaking.h"
+
+using namespace vgui;
+
+const int kInvalidImageID = -1;
+const int kMaxMVPCount = 9999;
+const float kScaleMVP = 0.75f; // scale of the MVP star relative to the player name height
+const float kUpdateInterval = 1.0f; // how often the scoreboard refreshes
+const float kTeamScoreMargin = 0.15f; // margin as a ratio of avatar height
+const float kTeamScoreLineLeadingRatio = 0.25f; // padding as a ratio of avatar height
+
+// CT player data colors
+ConVar cl_scoreboard_ct_color_red( "cl_scoreboard_ct_color_red", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_ct_color_green( "cl_scoreboard_ct_color_green", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_ct_color_blue( "cl_scoreboard_ct_color_blue", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player data blue channel", true, 0.0f, true, 255.0f );
+
+// T player data colors
+ConVar cl_scoreboard_t_color_red( "cl_scoreboard_t_color_red", "240", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_t_color_green( "cl_scoreboard_t_color_green", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_t_color_blue( "cl_scoreboard_t_color_blue", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player data blue channel", true, 0.0f, true, 255.0f );
+
+// Dead player data colors
+ConVar cl_scoreboard_dead_color_red( "cl_scoreboard_dead_color_red", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_dead_color_green( "cl_scoreboard_dead_color_green", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_dead_color_blue( "cl_scoreboard_dead_color_blue", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player data blue channel", true, 0.0f, true, 255.0f );
+
+// Clan colors
+ConVar cl_scoreboard_clan_ct_color_red( "cl_scoreboard_clan_ct_color_red", "150", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_clan_ct_color_green( "cl_scoreboard_clan_ct_color_green", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_clan_ct_color_blue( "cl_scoreboard_clan_ct_color_blue", "255", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard CT player clan tag blue channel", true, 0.0f, true, 255.0f );
+
+ConVar cl_scoreboard_clan_t_color_red( "cl_scoreboard_clan_t_color_red", "240", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_clan_t_color_green( "cl_scoreboard_clan_t_color_green", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_clan_t_color_blue( "cl_scoreboard_clan_t_color_blue", "90", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard T player clan tag blue channel", true, 0.0f, true, 255.0f );
+
+ConVar cl_scoreboard_dead_clan_color_red( "cl_scoreboard_dead_clan_color_red", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag red channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_dead_clan_color_green( "cl_scoreboard_dead_clan_color_green", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag green channel", true, 0.0f, true, 255.0f );
+ConVar cl_scoreboard_dead_clan_color_blue( "cl_scoreboard_dead_clan_color_blue", "125", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Scoreboard dead player clan tag blue channel", true, 0.0f, true, 255.0f );
+
+
+// [tj] These ConVars are defined at various places in the global scope. Just declaring them here so we can use them
+extern ConVar mp_winlimit;
+extern ConVar mp_maxrounds;
+extern ConVar mp_timelimit;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CCSClientScoreBoardDialog::CCSClientScoreBoardDialog( IViewPort *pViewPort ) : CClientScoreBoardDialog( pViewPort ),
+ m_DeadPlayerDataColor( 125, 125, 125, 125 ),
+ m_PlayerDataBgColor( 0, 0, 0, 0 ),
+ m_DeadPlayerClanColor( 125, 125, 125, 125 )
+{
+ m_teamDisplayT.playerDataColor = Color( 240, 90, 90, 255 );
+ m_teamDisplayT.playerClanColor = Color( 240, 90, 90, 255 );
+
+ m_teamDisplayCT.playerDataColor = Color( 150, 200, 255, 255 );
+ m_teamDisplayCT.playerClanColor = Color( 150, 200, 255, 255 );
+
+ m_iImageDead = kInvalidImageID;
+ m_iImageDominated = kInvalidImageID;
+ m_iImageNemesis = kInvalidImageID;
+ m_iImageBomb = kInvalidImageID;
+ m_iImageVIP = kInvalidImageID;
+ m_iImageFriend = kInvalidImageID;
+ m_iImageNemesisDead = kInvalidImageID;
+ m_iImageDominationDead = kInvalidImageID;
+
+ m_pWinConditionLabel = new Label( this, "WinConditionLabel", "" );
+ m_pClockLabel = new Label( this, "Icon_Clock", "" );
+
+ m_pLabelMapName = new Label( this, "MapName", "" );
+ m_pServerLabel = new Label( this, "ServerNameLabel", "" );
+
+ ListenForGameEvent( "server_spawn" );
+ ListenForGameEvent( "game_newmap" );
+ ListenForGameEvent( "match_end_conditions" );
+ ListenForGameEvent( "cs_win_panel_match" );
+
+ SetDialogVariable( "server", "" );
+ SetVisible( false );
+ SetProportional(true);
+ SetPaintBorderEnabled(false);
+ SetScheme( "ClientScheme" );
+
+ // [pfreese] Make the scoreboard a popup so it renders over the chat interface (which is also a popup). Hacky.
+ MakePopup();
+
+ m_listItemFont = NULL;
+
+ m_LocalPlayerItemID = -1;
+ m_iImageMVP = -1;
+
+ m_gameOver = false;
+
+ if ( g_pClientMode &&
+ g_pClientMode->GetMapName() )
+ {
+ V_wcsncpy( m_pMapName, g_pClientMode->GetMapName(), sizeof( m_pMapName ) );
+ SetDialogVariable( "mapname", m_pMapName );
+ m_pLabelMapName->SetVisible( true );
+ }
+
+ SetKeyBoardInputEnabled( true );
+
+ for ( int i = 0; i < cMaxScoreLines; ++i )
+ {
+ PlayerDisplay* pPlayerDisplay;
+
+ pPlayerDisplay = &m_teamDisplayCT.playerDisplay[i];
+ pPlayerDisplay->pClanLabel = NULL;
+ pPlayerDisplay->pNameLabel = NULL;
+ pPlayerDisplay->pScoreLabel = NULL;
+ pPlayerDisplay->pDeathsLabel = NULL;
+ pPlayerDisplay->pPingLabel = NULL;
+ pPlayerDisplay->pMVPCountLabel = NULL;
+ pPlayerDisplay->pAvatar = NULL;
+ pPlayerDisplay->pStatusImage = NULL;
+ pPlayerDisplay->pMVPImage = NULL;
+ pPlayerDisplay->pSelect = NULL;
+
+ pPlayerDisplay = &m_teamDisplayT.playerDisplay[i];
+ pPlayerDisplay->pClanLabel = NULL;
+ pPlayerDisplay->pNameLabel = NULL;
+ pPlayerDisplay->pScoreLabel = NULL;
+ pPlayerDisplay->pDeathsLabel = NULL;
+ pPlayerDisplay->pPingLabel = NULL;
+ pPlayerDisplay->pMVPCountLabel = NULL;
+ pPlayerDisplay->pAvatar = NULL;
+ pPlayerDisplay->pStatusImage = NULL;
+ pPlayerDisplay->pMVPImage = NULL;
+ pPlayerDisplay->pSelect = NULL;
+ }
+
+ m_pServerName[0] = L'\0';
+ m_pStatsEnabled[0] = L'\0';
+ m_pStatsDisabled[0] = L'\0';
+
+ m_MVPXOffset = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CCSClientScoreBoardDialog::~CCSClientScoreBoardDialog()
+{
+ for (int i = 0; i < cMaxScoreLines; ++i)
+ {
+ PlayerDisplay* pPlayerDisplay;
+
+ pPlayerDisplay = &m_teamDisplayCT.playerDisplay[i];
+ delete pPlayerDisplay->pClanLabel;
+ delete pPlayerDisplay->pNameLabel;
+ delete pPlayerDisplay->pScoreLabel;
+ delete pPlayerDisplay->pDeathsLabel;
+ delete pPlayerDisplay->pPingLabel;
+ delete pPlayerDisplay->pMVPCountLabel;
+ delete pPlayerDisplay->pAvatar;
+ delete pPlayerDisplay->pStatusImage;
+ delete pPlayerDisplay->pMVPImage;
+ delete pPlayerDisplay->pSelect;
+
+ pPlayerDisplay = &m_teamDisplayT.playerDisplay[i];
+ delete pPlayerDisplay->pClanLabel;
+ delete pPlayerDisplay->pNameLabel;
+ delete pPlayerDisplay->pScoreLabel;
+ delete pPlayerDisplay->pDeathsLabel;
+ delete pPlayerDisplay->pPingLabel;
+ delete pPlayerDisplay->pMVPCountLabel;
+ delete pPlayerDisplay->pAvatar;
+ delete pPlayerDisplay->pStatusImage;
+ delete pPlayerDisplay->pMVPImage;
+ delete pPlayerDisplay->pSelect;
+ }
+}
+
+const wchar_t *LocalizeFindSafe( const char *pTokenName )
+{
+ const wchar_t *pStr = g_pVGuiLocalize->Find( pTokenName );
+ return pStr ? pStr : L"\0";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply scheme settings
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ //
+ // [smessick] Note: ApplySchemeSettings is called multiple times for the scoreboard.
+ // Therefore, we must make sure to delete previously allocated items.
+ //
+
+ LoadControlSettings( "Resource/UI/scoreboard.res" );
+
+ //Just used for a background alpha value. 50% opacity
+ m_listItemFont = pScheme->GetFont( "ScoreboardBody_1", IsProportional() );
+ m_listItemFontSmaller = pScheme->GetFont( "ScoreboardBody_2", IsProportional() );
+ m_listItemFontSmallest = pScheme->GetFont( "ScoreboardBody_3", IsProportional() );
+ m_MVPFont = pScheme->GetFont( "ScoreboardMVP", IsProportional() );
+
+ SetBgColor( Color( 0, 0, 0, 0 ) );
+ SetBorder( pScheme->GetBorder( "BaseBorder" ) );
+
+ // turn off the default player list since we have our own
+ if ( m_pPlayerList )
+ {
+ m_pPlayerList->SetVisible( false );
+ }
+
+ // Set the player colors from the convars.
+ UpdatePlayerColors();
+
+ m_MVPXOffset = scheme()->GetProportionalScaledValueEx( GetScheme(), 2 );
+
+ SetupTeamDisplay( m_teamDisplayCT, "CT" );
+ SetupTeamDisplay( m_teamDisplayT, "T" );
+
+ // Set the server name (in the case of a resolution change).
+ if ( m_pServerName[0] == L'\0' &&
+ g_pClientMode->GetServerName() != NULL )
+ {
+ V_wcsncpy( m_pServerName, g_pClientMode->GetServerName(), sizeof( m_pServerName ) );
+ }
+
+ // Cache the stats enabled string.
+ if ( m_pStatsEnabled[0] == L'\0' )
+ {
+ V_wcsncpy( m_pStatsEnabled, LocalizeFindSafe( "#Cstrike_Scoreboard_StatsEnabled" ), sizeof( m_pStatsEnabled ) );
+ }
+
+ // Cache the stats disabled string.
+ if ( m_pStatsDisabled[0] == L'\0' )
+ {
+ V_wcsncpy( m_pStatsDisabled, LocalizeFindSafe( "#Cstrike_Scoreboard_StatsDisabled" ), sizeof( m_pStatsDisabled ) );
+ }
+
+ SetVisible( false );
+}
+
+
+void CCSClientScoreBoardDialog::SetupTeamDisplay( TeamDisplayInfo& teamDisplay, const char* szTeamPrefix )
+{
+ const int selectMargin = scheme()->GetProportionalScaledValueEx( GetScheme(), 1 );
+ const int mvpLabelWidth = scheme()->GetProportionalScaledValueEx( GetScheme(), 20 );
+ const int mvpLabelYOffset = scheme()->GetProportionalScaledValueEx( GetScheme(), 2 );
+
+ char tmpName[32];
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerArea", szTeamPrefix );
+ Panel *pPlayerArea = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerAvatar0", szTeamPrefix );
+ Panel *pPlayerAvatar0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerClan0", szTeamPrefix );
+ Panel *pPlayerClan0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerName0", szTeamPrefix );
+ Panel *pPlayerName0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerStatus0", szTeamPrefix );
+ Panel *pPlayerStatus0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerScore0", szTeamPrefix );
+ Panel *pPlayerScore0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerDeaths0", szTeamPrefix );
+ Panel *pPlayerDeaths0 = FindChildByName( tmpName );
+
+ V_snprintf( tmpName, sizeof(tmpName), "%sPlayerLatency0", szTeamPrefix );
+ Panel *pPlayerLatency0 = FindChildByName( tmpName );
+
+ // Get the bounds of the player area.
+ int playerX = 0;
+ int playerY = 0;
+ int playerWide = 0;
+ int playerTall = 0;
+ if ( pPlayerArea )
+ pPlayerArea->GetBounds( playerX, playerY, playerWide, playerTall );
+
+ // determine the line height needed
+ int x, y, wide, tall;
+ teamDisplay.scoreAreaLineHeight = 0;
+ teamDisplay.scoreAreaMinX = INT_MAX;
+ teamDisplay.scoreAreaMaxX = INT_MIN;
+
+ if ( pPlayerAvatar0 != NULL )
+ {
+ pPlayerAvatar0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerClan0 != NULL )
+ {
+ pPlayerClan0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerName0 != NULL )
+ {
+ pPlayerName0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerScore0 != NULL )
+ {
+ pPlayerScore0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerDeaths0 != NULL )
+ {
+ pPlayerDeaths0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerLatency0 != NULL )
+ {
+ pPlayerLatency0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+ if ( pPlayerStatus0 != NULL )
+ {
+ pPlayerStatus0->GetBounds( x, y, wide, tall );
+ teamDisplay.scoreAreaLineHeight = MAX( tall, teamDisplay.scoreAreaLineHeight );
+ teamDisplay.scoreAreaMinX = MIN( teamDisplay.scoreAreaMinX, x );
+ teamDisplay.scoreAreaMaxX = MAX( teamDisplay.scoreAreaMaxX, x + wide );
+ }
+
+ int marginY = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kTeamScoreMargin);
+
+ teamDisplay.scoreAreaInnerHeight = playerTall - 2 * marginY;
+ teamDisplay.scoreAreaLinePreferredLeading = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kTeamScoreLineLeadingRatio);
+ teamDisplay.scoreAreaStartY = playerY + marginY;
+ teamDisplay.maxPlayersVisible = MIN(cMaxScoreLines, teamDisplay.scoreAreaInnerHeight / teamDisplay.scoreAreaLineHeight);
+
+ // Calculate the starting point for player data.
+ int startY = teamDisplay.scoreAreaStartY;
+ int spacingY = teamDisplay.scoreAreaLineHeight + teamDisplay.scoreAreaLinePreferredLeading;
+
+ for ( int i = 0; i < cMaxScoreLines; ++i, startY += spacingY )
+ {
+ PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i];
+
+ int wide = 0;
+ int tall = 0;
+ int x = 0;
+ int y = 0;
+
+ // avatar
+ if ( pPlayerAvatar0 != NULL )
+ {
+ pPlayerAvatar0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playeravatar%d", szTeamPrefix, i );
+ delete playerDisplay.pAvatar;
+ playerDisplay.pAvatar = (CAvatarImagePanel*)SETUP_PANEL( new CAvatarImagePanel( this, tmpName ) );
+ playerDisplay.pAvatar->SetBounds( x, startY, wide, tall );
+ playerDisplay.pAvatar->SetDefaultAvatar( scheme()->GetImage( &teamDisplay == &m_teamDisplayCT ? CSTRIKE_DEFAULT_CT_AVATAR : CSTRIKE_DEFAULT_T_AVATAR, true ) );
+ playerDisplay.pAvatar->SetShouldDrawFriendIcon( false );
+ playerDisplay.pAvatar->SetShouldScaleImage( true );
+ playerDisplay.pAvatar->SetVisible( false );
+ }
+
+ // clan
+ if ( pPlayerClan0 != NULL )
+ {
+ pPlayerClan0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playerclan%d", szTeamPrefix, i );
+ delete playerDisplay.pClanLabel;
+ playerDisplay.pClanLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, tmpName ) );
+ playerDisplay.pClanLabel->SetFont( m_listItemFont );
+ playerDisplay.pClanLabel->SetBounds( x, startY, wide, tall );
+ playerDisplay.pClanLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pClanLabel->SetFgColor( m_teamDisplayCT.playerClanColor );
+ playerDisplay.pClanLabel->SetContentAlignment( Label::a_east );
+ playerDisplay.pClanLabel->SetVisible( false );
+ }
+
+ // name
+ if ( pPlayerName0 != NULL )
+ {
+ pPlayerName0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playername%d", szTeamPrefix, i );
+ delete playerDisplay.pNameLabel;
+ playerDisplay.pNameLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, tmpName ) );
+ playerDisplay.pNameLabel->SetFont( m_listItemFont );
+ playerDisplay.pNameLabel->SetBounds( x, startY, wide, tall);
+ playerDisplay.pNameLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pNameLabel->SetFgColor( m_teamDisplayCT.playerDataColor );
+ playerDisplay.pNameLabel->SetContentAlignment( Label::a_west );
+ playerDisplay.pNameLabel->SetVisible( false );
+
+ // mvp
+ int wideMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP);
+ int tallMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP);
+ int yMVP = ( teamDisplay.scoreAreaLineHeight - tallMVP ) / 2;
+
+ V_snprintf( tmpName, 32, "_%s_mvp%d", szTeamPrefix, i );
+ delete playerDisplay.pMVPImage;
+ playerDisplay.pMVPImage = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) );
+ playerDisplay.pMVPImage->SetBounds( x, startY + yMVP, wideMVP, tallMVP );
+ playerDisplay.pMVPImage->SetImage( "../hud/scoreboard_mvp" );
+ playerDisplay.pMVPImage->SetShouldScaleImage( true );
+ playerDisplay.pMVPImage->SetVisible( false );
+
+ // mvp count
+ V_snprintf( tmpName, 32, "_%s_mvpcount%d", szTeamPrefix, i );
+ delete playerDisplay.pMVPCountLabel;
+ playerDisplay.pMVPCountLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) );
+ playerDisplay.pMVPCountLabel->SetFont( m_MVPFont );
+ playerDisplay.pMVPCountLabel->SetBounds( 0, -mvpLabelYOffset, mvpLabelWidth, tallMVP );
+ playerDisplay.pMVPCountLabel->SetVisible( false );
+
+ // Pin to the mvp image.
+ V_snprintf( tmpName, 32, "_%s_mvp%d", szTeamPrefix, i );
+ playerDisplay.pMVPCountLabel->PinToSibling( tmpName, PIN_CENTER_LEFT, PIN_CENTER_RIGHT );
+ }
+
+ // score
+ if ( pPlayerScore0 != NULL )
+ {
+ pPlayerScore0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playerscore%d", szTeamPrefix, i );
+ delete playerDisplay.pScoreLabel;
+ playerDisplay.pScoreLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) );
+ playerDisplay.pScoreLabel->SetFont( m_listItemFont );
+ playerDisplay.pScoreLabel->SetBounds( x, startY, wide, tall );
+ playerDisplay.pScoreLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pScoreLabel->SetFgColor( m_teamDisplayCT.playerDataColor );
+ playerDisplay.pScoreLabel->SetContentAlignment( Label::a_center );
+ playerDisplay.pScoreLabel->SetVisible( false );
+ }
+
+ // deaths
+ if ( pPlayerDeaths0 != NULL )
+ {
+ pPlayerDeaths0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playerdeaths%d", szTeamPrefix, i );
+ delete playerDisplay.pDeathsLabel;
+ playerDisplay.pDeathsLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) );
+ playerDisplay.pDeathsLabel->SetFont( m_listItemFont );
+ playerDisplay.pDeathsLabel->SetBounds( x, startY, wide, tall );
+ playerDisplay.pDeathsLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pDeathsLabel->SetFgColor( m_teamDisplayCT.playerDataColor );
+ playerDisplay.pDeathsLabel->SetContentAlignment( Label::a_center );
+ playerDisplay.pDeathsLabel->SetVisible( false );
+ }
+
+ // latency
+ if ( pPlayerLatency0 != NULL )
+ {
+ pPlayerLatency0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playerping%d", szTeamPrefix, i );
+ delete playerDisplay.pPingLabel;
+ playerDisplay.pPingLabel = (Label*)SETUP_PANEL( new Label( this, tmpName, "" ) );
+ playerDisplay.pPingLabel->SetFont( m_listItemFont );
+ playerDisplay.pPingLabel->SetBounds( x, startY, wide, tall );
+ playerDisplay.pPingLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pPingLabel->SetFgColor( m_teamDisplayCT.playerDataColor );
+ playerDisplay.pPingLabel->SetContentAlignment( Label::a_center );
+ playerDisplay.pPingLabel->SetVisible( false );
+ }
+
+ // status
+ if ( pPlayerStatus0 != NULL )
+ {
+ pPlayerStatus0->GetBounds( x, y, wide, tall );
+ V_snprintf( tmpName, 32, "_%s_playerstatus%d", szTeamPrefix, i );
+ delete playerDisplay.pStatusImage;
+ playerDisplay.pStatusImage = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) );
+ playerDisplay.pStatusImage->SetBounds( x, startY, wide, tall );
+ playerDisplay.pStatusImage->SetImage( "../hud/scoreboard_dead" );
+ playerDisplay.pStatusImage->SetShouldScaleImage( true );
+ playerDisplay.pStatusImage->SetVisible( false );
+ }
+
+ // select
+ {
+ int x1 = teamDisplay.scoreAreaMinX - selectMargin;
+ int y1 = startY - selectMargin;
+ int x2 = teamDisplay.scoreAreaMaxX + selectMargin;
+ int y2 = startY + teamDisplay.scoreAreaLineHeight + selectMargin;
+
+ V_snprintf( tmpName, 32, "_%s_playerselect%d", szTeamPrefix, i );
+ delete playerDisplay.pSelect;
+ playerDisplay.pSelect = (ImagePanel*)SETUP_PANEL( new ImagePanel( this, tmpName ) );
+ playerDisplay.pSelect->SetBounds( x1, y1, x2 - x1, y2 - y1 );
+ playerDisplay.pSelect->SetImage( "../vgui/scoreboard/scoreboard-select" );
+ playerDisplay.pSelect->SetShouldScaleImage( true );
+ playerDisplay.pSelect->SetVisible( false );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for sorting players
+//-----------------------------------------------------------------------------
+int CCSClientScoreBoardDialog::PlayerSortFunction( PlayerScoreInfo* const* pLeft, PlayerScoreInfo* const* pRight )
+{
+ // a return value < 0 puts pLeft earlier in the list, > 0 puts pRight earlier
+ const PlayerScoreInfo* pPlayer1 = *pLeft;
+ const PlayerScoreInfo* pPlayer2 = *pRight;
+ Assert( pPlayer1 && pPlayer2 );
+
+ // bail out early if either player is an empty slot, i.e. has a player index of -1
+ if( pPlayer1->playerIndex == -1 )
+ return 1;
+ if( pPlayer2->playerIndex == -1 )
+ return -1;
+
+ // first compare scores
+ if ( pPlayer1->frags > pPlayer2->frags )
+ return -1;
+ if ( pPlayer1->frags < pPlayer2->frags )
+ return 1;
+
+ // second compare deaths
+ if ( pPlayer1->deaths > pPlayer2->deaths )
+ return 1;
+ if ( pPlayer1->deaths < pPlayer2->deaths )
+ return -1;
+
+ // if score and deaths are the same, use player index to get deterministic sort
+ if ( pPlayer1->playerIndex < pPlayer2->playerIndex )
+ return -1;
+ else
+ return 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the dialog
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::Update()
+{
+ if ( m_pServerLabel )
+ {
+ m_pServerLabel->SetText( m_pServerName );
+ }
+
+ // Update the stats status.
+ CAchievementMgr *pAchievementMgr = dynamic_cast<CAchievementMgr*>( engine->GetAchievementMgr() );
+ if ( pAchievementMgr != NULL &&
+ pAchievementMgr->CheckAchievementsEnabled() )
+ {
+ SetDialogVariable( "statsstatus", m_pStatsEnabled );
+ }
+ else
+ {
+ SetDialogVariable( "statsstatus", m_pStatsDisabled );
+ }
+
+ UpdateTeamInfo();
+ UpdatePlayerList();
+ UpdateSpectatorList();
+ UpdateHLTVList();
+ UpdateMatchEndText();
+ UpdateMvpElements();
+
+ // update every second
+ m_fNextUpdateTime = gpGlobals->curtime + kUpdateInterval;
+
+ // Catch the case where we call ShowPanel before ApplySchemeSettings, eg when going from windowed <-> fullscreen
+ if ( m_pImageList == NULL )
+ {
+ InvalidateLayout( true, true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates information about teams
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdateTeamInfo()
+{
+ if ( g_PR == NULL )
+ {
+ return;
+ }
+
+ // update the team sections in the scoreboard
+ for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ )
+ {
+ wchar_t *teamName = NULL;
+ C_Team *team = GetGlobalTeam( teamIndex );
+ if ( team )
+ {
+ // choose dialog variables to set depending on team
+ const char *pDialogVarTeamName = NULL;
+ const char *pDialogVarAliveCount = NULL;
+ const char *pDialogVarTeamScore = NULL;
+ switch ( teamIndex )
+ {
+ case TEAM_TERRORIST:
+ teamName = g_pVGuiLocalize->Find( "#Cstrike_Team_T" );
+ pDialogVarTeamName = "t_teamname";
+ pDialogVarAliveCount = "t_alivecount";
+ pDialogVarTeamScore = "t_totalteamscore";
+ break;
+ case TEAM_CT:
+ teamName = g_pVGuiLocalize->Find( "#Cstrike_Team_CT" );
+ pDialogVarTeamName = "ct_teamname";
+ pDialogVarAliveCount = "ct_alivecount";
+ pDialogVarTeamScore = "ct_totalteamscore";
+ break;
+ default:
+ Assert( false );
+ break;
+ }
+
+ // Set the team name if it hasn't been set.
+ wchar_t name[64];
+ if ( !teamName && team && team->Get_Name() != NULL )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( team->Get_Name(), name, sizeof( name ) );
+ teamName = name;
+ }
+
+ // Count the players on the team.
+ int numPlayers = 0;
+ int numAlive = 0;
+ for ( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
+ {
+ if ( g_PR->IsConnected( playerIndex ) && g_PR->GetTeam( playerIndex ) == teamIndex )
+ {
+ numPlayers++;
+ if ( g_PR->IsAlive( playerIndex ) )
+ {
+ ++numAlive;
+ }
+ }
+ }
+
+ SetDialogVariable( pDialogVarTeamName, teamName );
+
+ // Team score
+ wchar_t wNumScore[16];
+ V_snwprintf( wNumScore, ARRAYSIZE( wNumScore ), L"%i", team->Get_Score() );
+ SetDialogVariable( pDialogVarTeamScore, wNumScore );
+
+ // Number of alive players
+ wchar_t numAliveString[32];
+ V_snwprintf( numAliveString, ARRAYSIZE( numAliveString ), L"%i / %i", numAlive, numPlayers);
+ SetDialogVariable( pDialogVarAliveCount, numAliveString );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Helper to shrink font size when a string gets too long for its label
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::AdjustFontToFit( const char *pString, vgui::Label *pLabel )
+{
+ if ( !pString || !pLabel )
+ return;
+
+ int len = Q_strlen( pString );
+ if ( !len )
+ return;
+
+ int arraySize = len + 1;
+ wchar_t *pWideString = new wchar_t[arraySize];
+ V_UTF8ToUnicode( pString, pWideString, (arraySize * sizeof(wchar_t)) );
+
+ int stringWidth, stringHeight;
+ g_pMatSystemSurface->GetTextSize( m_listItemFont, pWideString, stringWidth, stringHeight );
+
+ int labelWidth = pLabel->GetWide();
+ if ( stringWidth <= labelWidth )
+ {
+ pLabel->SetFont( m_listItemFont );
+ }
+ else
+ {
+ g_pMatSystemSurface->GetTextSize( m_listItemFontSmaller, pWideString, stringWidth, stringHeight );
+ if ( stringWidth <= labelWidth )
+ {
+ pLabel->SetFont( m_listItemFontSmaller );
+ }
+ else
+ {
+ pLabel->SetFont(m_listItemFontSmallest);
+ }
+ }
+ delete [] pWideString;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the player list
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdatePlayerList()
+{
+ m_teamDisplayT.playerScores.PurgeAndDeleteElements();
+ m_teamDisplayCT.playerScores.PurgeAndDeleteElements();
+
+ C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pLocalPlayer )
+ return;
+
+ // Set the player colors from the convars.
+ UpdatePlayerColors();
+
+ for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
+ {
+ if( g_PR->IsConnected( playerIndex ) )
+ {
+ PlayerScoreInfo* playerScoreInfo = new PlayerScoreInfo;
+ if ( !GetPlayerScoreInfo( playerIndex, *playerScoreInfo ) )
+ {
+ delete playerScoreInfo;
+ continue;
+ }
+
+ if ( g_PR->GetTeam( playerIndex ) == TEAM_TERRORIST )
+ {
+ m_teamDisplayT.playerScores.AddToTail(playerScoreInfo);
+ }
+ else if ( g_PR->GetTeam( playerIndex ) == TEAM_CT )
+ {
+ m_teamDisplayCT.playerScores.AddToTail(playerScoreInfo);
+ }
+ else
+ {
+ // [mhansen] make sure we don't leak here
+ delete playerScoreInfo;
+ }
+ }
+ }
+
+ // Sort the lists of players
+ m_teamDisplayT.playerScores.Sort(PlayerSortFunction);
+ m_teamDisplayCT.playerScores.Sort(PlayerSortFunction);
+
+ // Force the local player to be visible when he is below the visible portion of the sorted list
+ if ( pLocalPlayer->GetTeamNumber() == TEAM_TERRORIST )
+ ForceLocalPlayerVisible(m_teamDisplayT);
+ else if ( pLocalPlayer->GetTeamNumber() == TEAM_CT )
+ ForceLocalPlayerVisible(m_teamDisplayCT);
+
+ UpdateTeamPlayerDisplay(m_teamDisplayT);
+ UpdateTeamPlayerDisplay(m_teamDisplayCT);
+}
+
+void CCSClientScoreBoardDialog::UpdateTeamPlayerDisplay( TeamDisplayInfo& teamDisplay )
+{
+ const int selectMargin = scheme()->GetProportionalScaledValueEx( GetScheme(), 1 );
+
+ C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
+ if ( !cs_PR )
+ return;
+ int iLocalPlayerIndex = GetLocalPlayerIndex();
+
+ int maxTeamSize = MAX(m_teamDisplayT.playerScores.Count(), m_teamDisplayCT.playerScores.Count());
+
+ // adjust spacing
+ int leadingAvailable = teamDisplay.scoreAreaInnerHeight - maxTeamSize * teamDisplay.scoreAreaLineHeight;
+ int leading = 0;
+ if ( maxTeamSize > 1 ) // only makes sense if we have more than one player
+ {
+ leading = clamp(leadingAvailable / (maxTeamSize - 1), 0, teamDisplay.scoreAreaLinePreferredLeading);
+ }
+ int spacingY = teamDisplay.scoreAreaLineHeight + leading;
+
+ // temp values for updating just the y position of elements
+ int xPos, yPos;
+
+ int i = 0;
+ int startY = teamDisplay.scoreAreaStartY;
+ for ( i = 0; i < MIN( cMaxScoreLines, teamDisplay.playerScores.Count() ); ++i, startY += spacingY )
+ {
+ if ( startY + teamDisplay.scoreAreaLineHeight > teamDisplay.scoreAreaStartY + teamDisplay.scoreAreaInnerHeight )
+ break;
+
+ PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i];
+
+ const PlayerScoreInfo* pPlayerScore = teamDisplay.playerScores[i];
+ if ( pPlayerScore )
+ {
+ int playerIndex = pPlayerScore->playerIndex;
+
+ const char* pUTF8Name = pPlayerScore->szName;
+
+// int bufsize;
+// if ( g_PR->IsFakePlayer( playerIndex ) )
+// bufsize = strlen( oldName ) * 2 + 14 + 1;
+// else
+// bufsize = strlen( oldName ) * 2 + 1;
+//
+// char *newName = (char *)_alloca( bufsize );
+// UTIL_MakeSafeName( oldName, newName, bufsize );
+
+ if ( pUTF8Name != NULL && V_strlen( pUTF8Name ) > 0 )
+ {
+ bool isAlive = cs_PR->IsAlive( playerIndex );
+ Color fgColor = ( isAlive ? teamDisplay.playerDataColor : m_DeadPlayerDataColor );
+ Color fgClanColor = ( isAlive ? teamDisplay.playerClanColor : m_DeadPlayerClanColor );
+
+ if ( playerDisplay.pNameLabel != NULL )
+ {
+ AdjustFontToFit( pUTF8Name, playerDisplay.pNameLabel );
+
+ playerDisplay.pNameLabel->SetVisible( true );
+ playerDisplay.pNameLabel->SetText( pUTF8Name );
+ playerDisplay.pNameLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pNameLabel->SetFgColor( fgColor );
+ playerDisplay.pNameLabel->GetPos(xPos, yPos);
+ playerDisplay.pNameLabel->SetPos(xPos, startY);
+
+ int tallMVP = RoundFloatToInt(teamDisplay.scoreAreaLineHeight * kScaleMVP);
+ int yMVP = ( teamDisplay.scoreAreaLineHeight - tallMVP ) / 2;
+ playerDisplay.pMVPImage->SetPos( xPos, startY + yMVP);
+ }
+
+ if ( playerDisplay.pClanLabel != NULL )
+ {
+ const char* pUTF8Clan = pPlayerScore->szClanTag;
+ AdjustFontToFit( pUTF8Clan, playerDisplay.pClanLabel );
+
+ playerDisplay.pClanLabel->SetVisible( true );
+ playerDisplay.pClanLabel->SetText( pUTF8Clan );
+ playerDisplay.pClanLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pClanLabel->SetFgColor( fgClanColor );
+ playerDisplay.pClanLabel->GetPos(xPos, yPos);
+ playerDisplay.pClanLabel->SetPos(xPos, startY);
+ }
+
+ char tmpbuf[16];
+
+ if ( playerDisplay.pScoreLabel != NULL )
+ {
+ Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->frags);
+ playerDisplay.pScoreLabel->SetVisible( true );
+ playerDisplay.pScoreLabel->SetText( tmpbuf );
+ playerDisplay.pScoreLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pScoreLabel->SetFgColor( fgColor );
+ playerDisplay.pScoreLabel->GetPos(xPos, yPos);
+ playerDisplay.pScoreLabel->SetPos(xPos, startY);
+ }
+
+ if ( playerDisplay.pDeathsLabel != NULL )
+ {
+ Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->deaths);
+ playerDisplay.pDeathsLabel->SetVisible( true );
+ playerDisplay.pDeathsLabel->SetText( tmpbuf );
+ playerDisplay.pDeathsLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pDeathsLabel->SetFgColor( fgColor );
+ playerDisplay.pDeathsLabel->GetPos(xPos, yPos);
+ playerDisplay.pDeathsLabel->SetPos(xPos, startY);
+
+ }
+
+ if ( playerDisplay.pPingLabel != NULL )
+ {
+ if ( pPlayerScore->ping >= 0 )
+ Q_snprintf( tmpbuf, sizeof( tmpbuf ), "%d", pPlayerScore->ping);
+ else
+ Q_strcpy( tmpbuf, "BOT");
+
+ playerDisplay.pPingLabel->SetVisible( true );
+ playerDisplay.pPingLabel->SetText( tmpbuf );
+ playerDisplay.pPingLabel->SetBgColor( m_PlayerDataBgColor );
+ playerDisplay.pPingLabel->SetFgColor( fgColor );
+ playerDisplay.pPingLabel->GetPos(xPos, yPos);
+ playerDisplay.pPingLabel->SetPos(xPos, startY);
+
+ }
+ }
+
+ if ( playerDisplay.pStatusImage != NULL )
+ {
+ if ( pPlayerScore->szStatus == NULL )
+ {
+ playerDisplay.pStatusImage->SetVisible( false );
+ }
+ else
+ {
+ playerDisplay.pStatusImage->SetVisible( true );
+ playerDisplay.pStatusImage->SetImage( pPlayerScore->szStatus );
+ playerDisplay.pStatusImage->GetPos(xPos, yPos);
+ playerDisplay.pStatusImage->SetPos(xPos, startY);
+ playerDisplay.pStatusImage->SetDrawColor(pPlayerScore->bStatusPlayerColor ? teamDisplay.playerDataColor : COLOR_WHITE);
+ }
+ }
+
+ if ( playerDisplay.pAvatar != NULL )
+ {
+ playerDisplay.pAvatar->SetVisible( true );
+ playerDisplay.pAvatar->SetPlayer( playerIndex, k_EAvatarSize32x32 );
+ playerDisplay.pAvatar->GetPos(xPos, yPos);
+ playerDisplay.pAvatar->SetPos(xPos, startY);
+ }
+
+ if ( playerDisplay.pSelect != NULL )
+ {
+ playerDisplay.pSelect->SetVisible( pPlayerScore->playerIndex == iLocalPlayerIndex );
+ playerDisplay.pSelect->SetPos(teamDisplay.scoreAreaMinX - selectMargin, startY - selectMargin);
+ }
+ }
+ }
+
+ // set any remaining entries to non-visible
+ for ( ; i < cMaxScoreLines; ++i )
+ {
+ PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i];
+
+ if ( playerDisplay.pClanLabel != NULL )
+ playerDisplay.pClanLabel->SetVisible(false);
+
+ if ( playerDisplay.pNameLabel != NULL )
+ playerDisplay.pNameLabel->SetVisible(false);
+
+ if ( playerDisplay.pScoreLabel != NULL )
+ playerDisplay.pScoreLabel->SetVisible(false);
+
+ if ( playerDisplay.pDeathsLabel != NULL )
+ playerDisplay.pDeathsLabel->SetVisible(false);
+
+ if ( playerDisplay.pPingLabel != NULL )
+ playerDisplay.pPingLabel->SetVisible(false);
+
+ if ( playerDisplay.pStatusImage != NULL )
+ playerDisplay.pStatusImage->SetVisible(false);
+
+ if ( playerDisplay.pAvatar != NULL )
+ playerDisplay.pAvatar->SetVisible( false );
+
+ if ( playerDisplay.pMVPImage != NULL )
+ playerDisplay.pMVPImage->SetVisible( false );
+
+ if ( playerDisplay.pMVPCountLabel != NULL )
+ playerDisplay.pMVPCountLabel->SetVisible( false );
+
+ if ( playerDisplay.pSelect != NULL )
+ playerDisplay.pSelect->SetVisible( false );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the spectator list
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdateSpectatorList()
+{
+ if ( g_PR == NULL )
+ {
+ return;
+ }
+
+ const int listTextLen = 100;
+ wchar_t listText[listTextLen];
+ listText[0] = L'\0';
+
+ const wchar_t *delimText = L", ";
+ const int delimTextLen = V_wcslen( delimText );
+
+ // Count the number of spectators and build up a list.
+ int nSpectators = 0;
+ for ( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; ++playerIndex )
+ {
+ if ( ShouldShowAsSpectator( playerIndex ) )
+ {
+ const char *playerName = g_PR->GetPlayerName( playerIndex );
+ if ( playerName != NULL )
+ {
+ // Convert the name to wide char.
+ wchar_t playerBuf[MAX_PLAYER_NAME_LENGTH];
+ playerBuf[0] = L'\0';
+ V_UTF8ToUnicode( playerName, playerBuf, sizeof( playerBuf ) );
+
+ //
+ // Check to see if there is space for the player name and delimiter.
+ //
+
+ bool addDelim = ( nSpectators > 0 );
+
+ int playerNameLen = V_wcslen( playerBuf );
+ if ( addDelim )
+ {
+ playerNameLen += delimTextLen;
+ }
+
+ int currentLen = V_wcslen( listText );
+ int remainingLen = listTextLen - currentLen - playerNameLen - 1;
+ if ( remainingLen >= 0 )
+ {
+ // Append the delimiter.
+ if ( addDelim )
+ {
+ V_wcscat_safe( listText, delimText );
+ }
+
+ // Append the player name.
+ V_wcscat_safe( listText, playerBuf );
+ }
+ }
+
+ ++nSpectators;
+ }
+ }
+
+ wchar_t labelText[512];
+ labelText[0] = L'\0';
+
+ if ( nSpectators == 0 )
+ {
+ // No spectators.
+ wchar_t *noSpectators = g_pVGuiLocalize->Find( "#Cstrike_Scoreboard_NoSpectators" );
+ if ( noSpectators != NULL )
+ {
+ V_wcsncpy( labelText, noSpectators, sizeof( labelText ) );
+ }
+ }
+ else
+ {
+ // Build the text for the number of spectators.
+ const int countTextLen = 16;
+ wchar_t countText[countTextLen];
+ countText[0] = L'\0';
+ V_snwprintf( countText, countTextLen, L"%i", nSpectators );
+ countText[countTextLen - 1] = L'\0';
+
+ // Build the combined count and spectator list text.
+ wchar_t *formatLabel = g_pVGuiLocalize->Find( ( nSpectators == 1 ) ? "#Cstrike_Scoreboard_Spectator" : "#Cstrike_Scoreboard_Spectators" );
+ if ( formatLabel != NULL )
+ {
+ g_pVGuiLocalize->ConstructString( labelText, sizeof( labelText ), formatLabel, 2, countText, listText );
+ }
+ }
+
+ SetDialogVariable( "spectators", labelText );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Display the number of HLTV viewers
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdateHLTVList( void )
+{
+ // Build the text for the number of viewers.
+ const int countTextLen = 16;
+ wchar_t countText[countTextLen];
+ countText[0] = L'\0';
+ V_snwprintf( countText, countTextLen, L"%i", m_HLTVSpectators );
+ countText[countTextLen - 1] = L'\0';
+
+ // Build the combined text.
+ wchar_t labelText[512];
+ labelText[0] = L'\0';
+ wchar_t *formatLabel = g_pVGuiLocalize->Find( "#Cstrike_Scoreboard_HLTV" );
+ if ( formatLabel != NULL )
+ {
+ g_pVGuiLocalize->ConstructString( labelText, sizeof( labelText ), formatLabel, 1, countText );
+ }
+
+ SetDialogVariable( "sourcetv", labelText );
+}
+
+/**
+* Special processing for MVP UI elements
+*/
+void CCSClientScoreBoardDialog::UpdateMvpElements()
+{
+ C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
+ if ( cs_PR == NULL )
+ {
+ return;
+ }
+
+ for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; ++teamIndex )
+ {
+ TeamDisplayInfo& teamDisplay = ( teamIndex == TEAM_TERRORIST ) ? m_teamDisplayT : m_teamDisplayCT;
+
+ for ( int i = 0; i < cMaxScoreLines && i < teamDisplay.playerScores.Count(); ++i )
+ {
+ PlayerDisplay& playerDisplay = teamDisplay.playerDisplay[i];
+
+ // Get the player index.
+ int playerIndex = -1;
+ if ( teamDisplay.playerScores[i] != NULL )
+ {
+ playerIndex = teamDisplay.playerScores[i]->playerIndex;
+ }
+
+ if ( playerIndex == -1 )
+ continue;
+
+ // early out fail conditions
+ if ( playerDisplay.pNameLabel == NULL || playerDisplay.pMVPImage == NULL || playerDisplay.pMVPCountLabel == NULL )
+ continue;
+
+ // Get the number of MVPs and cap it.
+ int numMVPs = MIN( kMaxMVPCount, cs_PR->GetNumMVPs( playerIndex ) );
+
+ // No MVPs.
+ if ( numMVPs == 0 )
+ {
+ playerDisplay.pMVPImage->SetVisible( false );
+ playerDisplay.pMVPCountLabel->SetVisible( false );
+ continue;
+ }
+
+ // Get the dimensions of the player label.
+ int xName = 0;
+ int yName = 0;
+ int wideName = 0;
+ int tallName = 0;
+ playerDisplay.pNameLabel->GetBounds( xName, yName, wideName, tallName );
+
+ wchar_t playerName[64];
+ playerDisplay.pNameLabel->GetText( playerName, sizeof(playerName) );
+
+ // Find the actual width of the rendered player name.
+ int actualWideName = 0;
+ int actualTallName = 0;
+ g_pMatSystemSurface->GetTextSize( playerDisplay.pNameLabel->GetFont(), playerName, actualWideName, actualTallName );
+ if ( actualWideName > wideName )
+ {
+ actualWideName = wideName;
+ }
+
+ // Get the dimensions of the mvp image.
+ int xMVPImage = 0;
+ int yMVPImage = 0;
+ int wideMVPImage = 0;
+ int tallMVPImage = 0;
+ playerDisplay.pMVPImage->GetBounds( xMVPImage, yMVPImage, wideMVPImage, tallMVPImage );
+
+ // The MVP label is hidden for only one star.
+ bool showMVPNumber = ( numMVPs > 1 );
+
+ // Set the MVP label text and get the actual size.
+ int actualWideMVPLabel = 0;
+ if ( showMVPNumber )
+ {
+ const int mvpTextSize = 8;
+ wchar_t mvpText[mvpTextSize];
+ V_snwprintf( mvpText, ARRAYSIZE( mvpText ), L"%i", numMVPs );
+ playerDisplay.pMVPCountLabel->SetText( mvpText );
+ playerDisplay.pMVPCountLabel->SetVisible( true );
+
+ int actualTallMVPLabel = 0;
+ g_pMatSystemSurface->GetTextSize( playerDisplay.pMVPCountLabel->GetFont(), mvpText, actualWideMVPLabel, actualTallMVPLabel );
+ }
+
+ // Calculate the total width of the mvp stuff.
+ int wideMVPTotal = wideMVPImage;
+ if ( showMVPNumber )
+ {
+ wideMVPTotal += actualWideMVPLabel;
+ }
+
+ // Calculate the optimal place for the mvp.
+ int x = xName + actualWideName + m_MVPXOffset;
+
+ // Get the position of the status image.
+ if ( playerDisplay.pStatusImage )
+ {
+ int xStatus = 0;
+ int yStatus = 0;
+ playerDisplay.pStatusImage->GetPos( xStatus, yStatus );
+
+ if ( x + wideMVPTotal > xStatus )
+ {
+ // Don't run over the status image. Back up.
+ x = xStatus - wideMVPTotal;
+ }
+ }
+
+ playerDisplay.pMVPImage->SetPos( x, yMVPImage );
+ playerDisplay.pMVPImage->SetVisible( true );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether the specified player index is a spectator
+//-----------------------------------------------------------------------------
+bool CCSClientScoreBoardDialog::ShouldShowAsSpectator( int iPlayerIndex )
+{
+ C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
+ if ( !cs_PR )
+ return false;
+
+ // see if player is connected
+ if ( cs_PR->IsConnected( iPlayerIndex ) )
+ {
+ // either spectator or unassigned team should show in spectator list
+ int iTeam = cs_PR->GetTeam( iPlayerIndex );
+ if ( TEAM_SPECTATOR == iTeam || TEAM_UNASSIGNED == iTeam )
+ return true;
+ }
+ return false;
+}
+
+
+void CCSClientScoreBoardDialog::FireGameEvent( IGameEvent *event )
+{
+ if ( event == NULL )
+ return;
+
+ const char *pEventName = event->GetName();
+ if ( pEventName == NULL )
+ return;
+
+ if ( Q_strcmp( pEventName, "server_spawn" ) == 0 )
+ {
+ // set server name in scoreboard
+ const char *hostname = event->GetString( "hostname" );
+ if ( hostname != NULL )
+ {
+ wchar_t wzHostName[256];
+ g_pVGuiLocalize->ConvertANSIToUnicode( hostname, wzHostName, sizeof( wzHostName ) );
+ g_pVGuiLocalize->ConstructString( m_pServerName, sizeof(m_pServerName), g_pVGuiLocalize->Find( "#Cstrike_SB_Server" ), 1, wzHostName );
+
+ if ( m_pServerLabel )
+ {
+ m_pServerLabel->SetText( m_pServerName );
+ }
+
+ if ( m_gameOver )
+ {
+ ResetFromGameOverState();
+ }
+
+ // Save the server name for use after this panel is reconstructed
+ if ( g_pClientMode )
+ {
+ g_pClientMode->SetServerName( m_pServerName );
+ }
+ }
+ }
+ else if ( Q_strcmp( pEventName, "game_newmap" ) == 0 )
+ {
+ const char *mapName = event->GetString( "mapname" );
+ if ( mapName != NULL )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( mapName, m_pMapName, sizeof( m_pMapName ) );
+ SetDialogVariable( "mapname", m_pMapName );
+
+ if ( m_gameOver )
+ {
+ ResetFromGameOverState();
+ }
+
+ // Save the map name for use after this panel is reconstructed
+ if ( g_pClientMode )
+ {
+ g_pClientMode->SetMapName( m_pMapName );
+ }
+ }
+ }
+ else if ( Q_strcmp( pEventName, "match_end_conditions" ) == 0 )
+ {
+ UpdateMatchEndText();
+ }
+ else if ( Q_strcmp( pEventName, "cs_win_panel_match" ) == 0 )
+ {
+ m_gameOver = true;
+ UpdateMatchEndText();
+ }
+
+ BaseClass::FireGameEvent( event );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a new row to the scoreboard, from the playerinfo structure
+//-----------------------------------------------------------------------------
+bool CCSClientScoreBoardDialog::GetPlayerScoreInfo( int playerIndex, PlayerScoreInfo& playerScoreInfo )
+{
+ if ( g_PR == NULL )
+ return false;
+
+ // Clean up the player name
+ const char *oldName = g_PR->GetPlayerName( playerIndex );
+ if ( oldName == NULL )
+ return false;
+
+ playerScoreInfo.szName = g_PR->GetPlayerName( playerIndex );
+
+ playerScoreInfo.playerIndex = playerIndex;
+ playerScoreInfo.frags = g_PR->GetPlayerScore( playerIndex );
+ playerScoreInfo.deaths = g_PR->GetDeaths( playerIndex );
+
+ if ( g_PR->GetPing( playerIndex ) < 1 )
+ {
+ if ( g_PR->IsFakePlayer( playerIndex ) )
+ {
+ playerScoreInfo.ping = -1;
+ }
+ else
+ {
+ playerScoreInfo.ping = 0;
+ }
+ }
+ else
+ {
+ playerScoreInfo.ping = g_PR->GetPing( playerIndex );
+ }
+
+ // get CS specific infos
+ C_CS_PlayerResource *cs_PR = dynamic_cast<C_CS_PlayerResource *>( g_PR );
+
+ C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
+
+ if ( !cs_PR || !pLocalPlayer )
+ {
+ return false;
+ }
+
+ // Get the clan tag
+ playerScoreInfo.szClanTag = cs_PR->GetClanTag( playerIndex );
+
+ bool bShowExtraInfo =
+ ( pLocalPlayer->GetTeamNumber() == TEAM_UNASSIGNED ) || // we're not spawned yet
+ ( pLocalPlayer->GetTeamNumber() == TEAM_SPECTATOR ) || // we are a spectator
+ ( pLocalPlayer->IsPlayerDead() && mp_forcecamera.GetInt() == OBS_ALLOW_ALL ) || // we are dead and allowed to spectate opponents
+ ( pLocalPlayer->GetTeamNumber() == g_PR->GetTeam( playerIndex ) ); // we're on the same team
+
+ playerScoreInfo.szStatus = NULL;
+ playerScoreInfo.bStatusPlayerColor = false;
+
+ // set the status icon; lowest priority icons are tested first, and highest last
+
+ if ( cs_PR->IsVIP( playerIndex ) && bShowExtraInfo )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_clock";
+ playerScoreInfo.bStatusPlayerColor = true;
+ }
+
+ if ( !g_PR->IsAlive( playerIndex ) && g_PR->GetTeam( playerIndex ) > TEAM_SPECTATOR )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_dead";
+ }
+
+ if ( g_PR->IsHLTV( playerIndex ) )
+ {
+// // show #spectators in class field, it's transmitted as player's score
+// char numspecs[32];
+// Q_snprintf( numspecs, sizeof( numspecs ), "%i Spectators", m_HLTVSpectators );
+// kv->SetString( "class", numspecs );
+ }
+
+ // Set the dominated icon
+ if ( pLocalPlayer->IsPlayerDominatingMe( playerIndex ) )
+ {
+ if ( g_PR->IsAlive( playerIndex ) )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_nemesis";
+ }
+ else
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_nemesis-dead";
+ }
+ }
+
+ if ( pLocalPlayer->IsPlayerDominated(playerIndex) )
+ {
+ if ( g_PR->IsAlive( playerIndex ) )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_dominated";
+ }
+ else
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_domination-dead";
+ }
+ }
+
+ if ( cs_PR->HasC4( playerIndex ) && bShowExtraInfo )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_bomb";
+ playerScoreInfo.bStatusPlayerColor = true;
+ }
+
+ if ( cs_PR->HasDefuser( playerIndex ) && bShowExtraInfo )
+ {
+ playerScoreInfo.szStatus = "../hud/scoreboard_defuser";
+ playerScoreInfo.bStatusPlayerColor = true;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the time/round remaining display and server and map name
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdateMatchEndText()
+{
+ // Hide the win condition.
+ if ( m_pWinConditionLabel )
+ {
+ m_pWinConditionLabel->SetVisible( false );
+ }
+
+ // Hide the clock.
+ if ( m_pClockLabel )
+ {
+ m_pClockLabel->SetVisible( false );
+ }
+
+ if ( !m_gameOver )
+ {
+ SetDialogVariable( "mapname", m_pMapName );
+
+ wchar_t wzMatchEndCausesLabel[128], wzMatchEndCause[32];
+
+ // Time limit
+ if ( mp_timelimit.GetInt() != 0 )
+ {
+ int timeTillEndOfMatch = CSGameRules()->GetMapRemainingTime();
+ bool showTime = ( timeTillEndOfMatch != -1 );
+ if ( showTime )
+ {
+ if ( m_pWinConditionLabel )
+ {
+ V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%.2i:%.2i", timeTillEndOfMatch / 60, timeTillEndOfMatch % 60 );
+ g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ),
+ g_pVGuiLocalize->Find( "#Cstrike_Time_LeftVariable" ), 1, wzMatchEndCause );
+ m_pWinConditionLabel->SetText( wzMatchEndCausesLabel );
+ m_pWinConditionLabel->SetVisible( true );
+ }
+
+ if ( m_pClockLabel )
+ {
+ m_pClockLabel->SetVisible( true );
+ }
+ }
+ }
+ // Round limit
+ else if ( mp_maxrounds.GetInt() != 0 )
+ {
+ if ( m_pWinConditionLabel )
+ {
+ // Get the number of rounds played.
+ int roundsPlayed = 0;
+ for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ )
+ {
+ C_Team *team = GetGlobalTeam( teamIndex );
+ if ( team )
+ {
+ roundsPlayed += team->Get_Score();
+ }
+ }
+
+ V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%d", mp_maxrounds.GetInt() - roundsPlayed );
+ g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ),
+ g_pVGuiLocalize->Find( "#Cstrike_Rounds_LeftVariable" ), 1, wzMatchEndCause );
+ m_pWinConditionLabel->SetText( wzMatchEndCausesLabel );
+ m_pWinConditionLabel->SetVisible( true );
+ }
+ }
+ // Win limit
+ else if ( mp_winlimit.GetInt() != 0 )
+ {
+ if ( m_pWinConditionLabel )
+ {
+ V_snwprintf( wzMatchEndCause, ARRAYSIZE( wzMatchEndCause ), L"%d", mp_winlimit.GetInt() );
+ g_pVGuiLocalize->ConstructString( wzMatchEndCausesLabel, sizeof( wzMatchEndCausesLabel ),
+ g_pVGuiLocalize->Find( "#Cstrike_Wins_NeededVariable" ), 1, wzMatchEndCause );
+ m_pWinConditionLabel->SetText( wzMatchEndCausesLabel );
+ m_pWinConditionLabel->SetVisible( true );
+ }
+ }
+ }
+}
+
+// Resets all changes made by the scoreboard's state at the match end
+void CCSClientScoreBoardDialog::ResetFromGameOverState()
+{
+ m_gameOver = false;
+
+ if ( m_pLabelMapName )
+ {
+ m_pLabelMapName->SetVisible( true );
+ }
+
+ UpdateMatchEndText();
+}
+
+// [tj] We hook into the show command so we can lock or unlock all the elements that need to be hidden
+//
+// [pfreese] This used to enable/disable keyboard input, but since the scoreboard is now a popup, we have
+// to leave the keyboard disabled
+void CCSClientScoreBoardDialog::ShowPanel( bool state )
+{
+ BaseClass::ShowPanel(state);
+
+ int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "hide_for_scoreboard" );
+
+ if ( state )
+ {
+ gHUD.LockRenderGroup( iRenderGroup );
+ }
+ else
+ {
+ gHUD.UnlockRenderGroup( iRenderGroup );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Grabs the player data colors from the convars.
+//-----------------------------------------------------------------------------
+void CCSClientScoreBoardDialog::UpdatePlayerColors( void )
+{
+ m_teamDisplayT.playerDataColor.SetColor( cl_scoreboard_t_color_red.GetInt(), cl_scoreboard_t_color_green.GetInt(), cl_scoreboard_t_color_blue.GetInt(), 255 );
+ m_teamDisplayCT.playerDataColor.SetColor( cl_scoreboard_ct_color_red.GetInt(), cl_scoreboard_ct_color_green.GetInt(), cl_scoreboard_ct_color_blue.GetInt(), 255 );
+ m_DeadPlayerDataColor.SetColor( cl_scoreboard_dead_color_red.GetInt(), cl_scoreboard_dead_color_green.GetInt(), cl_scoreboard_dead_color_blue.GetInt(), 255 );
+ m_teamDisplayT.playerClanColor.SetColor( cl_scoreboard_clan_t_color_red.GetInt(), cl_scoreboard_clan_t_color_green.GetInt(), cl_scoreboard_clan_t_color_blue.GetInt(), 255 );
+ m_teamDisplayCT.playerClanColor.SetColor( cl_scoreboard_clan_ct_color_red.GetInt(), cl_scoreboard_clan_ct_color_green.GetInt(), cl_scoreboard_clan_ct_color_blue.GetInt(), 255 );
+ m_DeadPlayerClanColor.SetColor( cl_scoreboard_dead_clan_color_red.GetInt(), cl_scoreboard_dead_clan_color_green.GetInt(), cl_scoreboard_dead_clan_color_blue.GetInt(), 255 );
+}
+
+// [tj] Disabling joystick input if you are dead.
+void CCSClientScoreBoardDialog::OnThink()
+{
+ BaseClass::OnThink();
+
+#ifdef _XBOX
+ C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( pLocalPlayer )
+ {
+ bool mouseEnabled = IsMouseInputEnabled();
+ if (pLocalPlayer->IsAlive() == mouseEnabled)
+ {
+ SetMouseInputEnabled( !mouseEnabled );
+ }
+ }
+#endif
+}
+
+bool CCSClientScoreBoardDialog::ForceLocalPlayerVisible( TeamDisplayInfo& teamDisplay )
+{
+ int iLocalPlayerIndex = GetLocalPlayerIndex();
+
+ // Look for the local player in the non-visible portion of the member list
+ for (int i = teamDisplay.maxPlayersVisible; i < teamDisplay.playerScores.Count(); ++i)
+ {
+ PlayerScoreInfo* pPlayerScore = teamDisplay.playerScores[i];
+ Assert(pPlayerScore != NULL);
+
+ // Determine if this is the local player's entry
+ if ( pPlayerScore->playerIndex == iLocalPlayerIndex )
+ {
+ // Remove the local player entry from the current position
+ teamDisplay.playerScores.Remove(i);
+
+ // Re-insert the local player entry at the end of the visible list
+ teamDisplay.playerScores.InsertBefore( teamDisplay.maxPlayersVisible - 1, pPlayerScore );
+
+ return true;
+ }
+ }
+ return false;
+}