mirror of
https://github.com/nillerusr/source-engine.git
synced 2025-04-07 08:54:25 +00:00
899 lines
26 KiB
C++
899 lines
26 KiB
C++
//========= Copyright © 1996-2006, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
|
|
#include <tier1/fmtstr.h>
|
|
#include <vgui_controls/Label.h>
|
|
#include <vgui_controls/Button.h>
|
|
#include <vgui_controls/ImagePanel.h>
|
|
#include <vgui_controls/RichText.h>
|
|
#include <vgui_controls/Frame.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/ImageList.h>
|
|
#include <game/client/iviewport.h>
|
|
#include <KeyValues.h>
|
|
#include <filesystem.h>
|
|
#include "IGameUIFuncs.h" // for key bindings
|
|
|
|
#include "tf_controls.h"
|
|
#include "tf_shareddefs.h"
|
|
#include "tf_clientscoreboard.h"
|
|
#include "tf_gamestats_shared.h"
|
|
#include "tf_hud_statpanel.h"
|
|
#include "c_playerresource.h"
|
|
#include "c_tf_playerresource.h"
|
|
#include "c_tf_team.h"
|
|
#include "c_tf_player.h"
|
|
#include "vgui_avatarimage.h"
|
|
|
|
#if defined ( _X360 )
|
|
#include "engine/imatchmaking.h"
|
|
#endif
|
|
|
|
using namespace vgui;
|
|
|
|
#define SCOREBOARD_MAX_LIST_ENTRIES 12
|
|
|
|
extern bool IsInCommentaryMode( void );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CTFClientScoreBoardDialog::CTFClientScoreBoardDialog( IViewPort *pViewPort ) : CClientScoreBoardDialog( pViewPort )
|
|
{
|
|
SetProportional(true);
|
|
SetKeyBoardInputEnabled(false);
|
|
SetMouseInputEnabled(false);
|
|
SetScheme( "ClientScheme" );
|
|
|
|
m_pPlayerListBlue = new SectionedListPanel( this, "BluePlayerList" );
|
|
m_pPlayerListRed = new SectionedListPanel( this, "RedPlayerList" );
|
|
m_pLabelPlayerName = new CTFLabel( this, "PlayerNameLabel", "" );
|
|
m_pImagePanelHorizLine = new ImagePanel( this, "HorizontalLine" );
|
|
m_pClassImage = new CTFClassImage( this, "ClassImage" );
|
|
m_iImageDead = 0;
|
|
m_iImageDominated = 0;
|
|
m_iImageNemesis = 0;
|
|
|
|
ListenForGameEvent( "server_spawn" );
|
|
|
|
SetDialogVariable( "server", "" );
|
|
|
|
SetVisible( false );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
CTFClientScoreBoardDialog::~CTFClientScoreBoardDialog()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::PerformLayout()
|
|
{
|
|
BaseClass::PerformLayout();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
|
|
{
|
|
BaseClass::ApplySchemeSettings( pScheme );
|
|
|
|
LoadControlSettings("Resource/UI/scoreboard.res");
|
|
|
|
if ( m_pImageList )
|
|
{
|
|
m_iImageDead = m_pImageList->AddImage( scheme()->GetImage( "../hud/leaderboard_dead", true ) );
|
|
m_iImageDominated = m_pImageList->AddImage( scheme()->GetImage( "../hud/leaderboard_dominated", true ) );
|
|
m_iImageNemesis = m_pImageList->AddImage( scheme()->GetImage( "../hud/leaderboard_nemesis", true ) );
|
|
|
|
// resize the images to our resolution
|
|
for (int i = 1; i < m_pImageList->GetImageCount(); i++ )
|
|
{
|
|
int wide = 13, tall = 13;
|
|
m_pImageList->GetImage(i)->SetSize(scheme()->GetProportionalScaledValueEx( GetScheme(), wide ), scheme()->GetProportionalScaledValueEx( GetScheme(),tall ) );
|
|
}
|
|
}
|
|
|
|
SetPlayerListImages( m_pPlayerListBlue );
|
|
SetPlayerListImages( m_pPlayerListRed );
|
|
|
|
SetBgColor( Color( 0, 0, 0, 0) );
|
|
SetBorder( NULL );
|
|
SetVisible( false );
|
|
|
|
Reset();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::ShowPanel( bool bShow )
|
|
{
|
|
// Catch the case where we call ShowPanel before ApplySchemeSettings, eg when
|
|
// going from windowed <-> fullscreen
|
|
if ( m_pImageList == NULL )
|
|
{
|
|
InvalidateLayout( true, true );
|
|
}
|
|
|
|
// Don't show in commentary mode
|
|
if ( IsInCommentaryMode() )
|
|
{
|
|
bShow = false;
|
|
}
|
|
|
|
if ( IsVisible() == bShow )
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iRenderGroup = gHUD.LookupRenderGroupIndexByName( "global" );
|
|
|
|
if ( bShow )
|
|
{
|
|
SetVisible( true );
|
|
MoveToFront();
|
|
|
|
gHUD.LockRenderGroup( iRenderGroup );
|
|
|
|
// Clear the selected item, this forces the default to the local player
|
|
SectionedListPanel *pList = GetSelectedPlayerList();
|
|
if ( pList )
|
|
{
|
|
pList->ClearSelection();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetVisible( false );
|
|
|
|
gHUD.UnlockRenderGroup( iRenderGroup );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Resets the scoreboard panel
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::Reset()
|
|
{
|
|
InitPlayerList( m_pPlayerListBlue );
|
|
InitPlayerList( m_pPlayerListRed );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Inits the player list in a list panel
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::InitPlayerList( SectionedListPanel *pPlayerList )
|
|
{
|
|
pPlayerList->SetVerticalScrollbar( false );
|
|
pPlayerList->RemoveAll();
|
|
pPlayerList->RemoveAllSections();
|
|
pPlayerList->AddSection( 0, "Players", TFPlayerSortFunc );
|
|
pPlayerList->SetSectionAlwaysVisible( 0, true );
|
|
pPlayerList->SetSectionFgColor( 0, Color( 255, 255, 255, 255 ) );
|
|
pPlayerList->SetBgColor( Color( 0, 0, 0, 0 ) );
|
|
pPlayerList->SetBorder( NULL );
|
|
|
|
// Avatars are always displayed at 32x32 regardless of resolution
|
|
if ( ShowAvatars() )
|
|
{
|
|
pPlayerList->AddColumnToSection( 0, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, m_iAvatarWidth );
|
|
}
|
|
pPlayerList->AddColumnToSection( 0, "name", "#TF_Scoreboard_Name", 0, m_iNameWidth );
|
|
pPlayerList->AddColumnToSection( 0, "status", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_CENTER, m_iStatusWidth );
|
|
pPlayerList->AddColumnToSection( 0, "nemesis", "", SectionedListPanel::COLUMN_IMAGE, m_iNemesisWidth );
|
|
pPlayerList->AddColumnToSection( 0, "class", "", 0, m_iClassWidth );
|
|
pPlayerList->AddColumnToSection( 0, "score", "#TF_Scoreboard_Score", SectionedListPanel::COLUMN_RIGHT, m_iScoreWidth );
|
|
pPlayerList->AddColumnToSection( 0, "ping", "#TF_Scoreboard_Ping", SectionedListPanel::COLUMN_RIGHT, m_iPingWidth );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Builds the image list to use in the player list
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::SetPlayerListImages( vgui::SectionedListPanel *pPlayerList )
|
|
{
|
|
pPlayerList->SetImageList( m_pImageList, false );
|
|
pPlayerList->SetVisible( true );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates the dialog
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::Update()
|
|
{
|
|
UpdateTeamInfo();
|
|
UpdatePlayerList();
|
|
UpdateSpectatorList();
|
|
UpdatePlayerDetails();
|
|
MoveToCenterOfScreen();
|
|
|
|
// update every second
|
|
m_fNextUpdateTime = gpGlobals->curtime + 1.0f;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates information about teams
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::UpdateTeamInfo()
|
|
{
|
|
// update the team sections in the scoreboard
|
|
for ( int teamIndex = TF_TEAM_RED; teamIndex <= TF_TEAM_BLUE; teamIndex++ )
|
|
{
|
|
wchar_t *teamName = NULL;
|
|
C_Team *team = GetGlobalTeam( teamIndex );
|
|
if ( team )
|
|
{
|
|
// choose dialog variables to set depending on team
|
|
const char *pDialogVarTeamScore = NULL;
|
|
const char *pDialogVarTeamPlayerCount = NULL;
|
|
switch ( teamIndex ) {
|
|
case TF_TEAM_RED:
|
|
pDialogVarTeamScore = "redteamscore";
|
|
pDialogVarTeamPlayerCount = "redteamplayercount";
|
|
break;
|
|
case TF_TEAM_BLUE:
|
|
pDialogVarTeamScore = "blueteamscore";
|
|
pDialogVarTeamPlayerCount = "blueteamplayercount";
|
|
break;
|
|
default:
|
|
Assert( false );
|
|
break;
|
|
}
|
|
|
|
// update # of players on each team
|
|
wchar_t name[64];
|
|
wchar_t string1[1024];
|
|
wchar_t wNumPlayers[6];
|
|
_snwprintf( wNumPlayers, ARRAYSIZE( wNumPlayers ), L"%i", team->Get_Number_Players() );
|
|
if ( !teamName && team )
|
|
{
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( team->Get_Name(), name, sizeof( name ) );
|
|
teamName = name;
|
|
}
|
|
if ( team->Get_Number_Players() == 1 )
|
|
{
|
|
g_pVGuiLocalize->ConstructString( string1, sizeof(string1), g_pVGuiLocalize->Find( "#TF_ScoreBoard_Player" ), 1, wNumPlayers );
|
|
}
|
|
else
|
|
{
|
|
g_pVGuiLocalize->ConstructString( string1, sizeof(string1), g_pVGuiLocalize->Find( "#TF_ScoreBoard_Players" ), 1, wNumPlayers );
|
|
}
|
|
|
|
// set # of players for team in dialog
|
|
SetDialogVariable( pDialogVarTeamPlayerCount, string1 );
|
|
|
|
// set team score in dialog
|
|
SetDialogVariable( pDialogVarTeamScore, team->Get_Score() );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AreEnemyTeams( int iTeam1, int iTeam2 )
|
|
{
|
|
if ( iTeam1 == TF_TEAM_RED && iTeam2 == TF_TEAM_BLUE )
|
|
return true;
|
|
|
|
if ( iTeam1 == TF_TEAM_BLUE && iTeam2 == TF_TEAM_RED )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates the player list
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::UpdatePlayerList()
|
|
{
|
|
int iSelectedPlayerIndex = GetLocalPlayerIndex();
|
|
|
|
// Save off which player we had selected
|
|
SectionedListPanel *pList = GetSelectedPlayerList();
|
|
|
|
if ( pList )
|
|
{
|
|
int itemID = pList->GetSelectedItem();
|
|
|
|
if ( itemID >= 0 )
|
|
{
|
|
KeyValues *pInfo = pList->GetItemData( itemID );
|
|
if ( pInfo )
|
|
{
|
|
iSelectedPlayerIndex = pInfo->GetInt( "playerIndex" );
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pPlayerListRed->RemoveAll();
|
|
m_pPlayerListBlue->RemoveAll();
|
|
|
|
C_TF_PlayerResource *tf_PR = dynamic_cast<C_TF_PlayerResource *>( g_PR );
|
|
if ( !tf_PR )
|
|
return;
|
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
|
if ( !pLocalPlayer )
|
|
return;
|
|
|
|
int localteam = pLocalPlayer->GetTeamNumber();
|
|
|
|
bool bMadeSelection = false;
|
|
|
|
for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
|
|
{
|
|
if( g_PR->IsConnected( playerIndex ) )
|
|
{
|
|
SectionedListPanel *pPlayerList = NULL;
|
|
switch ( g_PR->GetTeam( playerIndex ) )
|
|
{
|
|
case TF_TEAM_BLUE:
|
|
pPlayerList = m_pPlayerListBlue;
|
|
break;
|
|
case TF_TEAM_RED:
|
|
pPlayerList = m_pPlayerListRed;
|
|
break;
|
|
}
|
|
if ( null == pPlayerList )
|
|
continue;
|
|
|
|
const char *szName = tf_PR->GetPlayerName( playerIndex );
|
|
int score = tf_PR->GetTotalScore( playerIndex );
|
|
|
|
KeyValues *pKeyValues = new KeyValues( "data" );
|
|
|
|
pKeyValues->SetInt( "playerIndex", playerIndex );
|
|
pKeyValues->SetString( "name", szName );
|
|
pKeyValues->SetInt( "score", score );
|
|
|
|
// can only see class information if we're on the same team
|
|
if ( !AreEnemyTeams( g_PR->GetTeam( playerIndex ), localteam ) && !( localteam == TEAM_UNASSIGNED ) )
|
|
{
|
|
// class name
|
|
if( g_PR->IsConnected( playerIndex ) )
|
|
{
|
|
int iClass = tf_PR->GetPlayerClass( playerIndex );
|
|
if ( GetLocalPlayerIndex() == playerIndex && !tf_PR->IsAlive( playerIndex ) ) {
|
|
// If this is local player and he is dead, show desired class (which he will spawn as) rather than current class.
|
|
C_TFPlayer *pPlayer = C_TFPlayer::GetLocalTFPlayer();
|
|
int iDesiredClass = pPlayer->m_Shared.GetDesiredPlayerClassIndex();
|
|
// use desired class unless it's random -- if random, his future class is not decided until moment of spawn
|
|
if ( TF_CLASS_RANDOM != iDesiredClass )
|
|
{
|
|
iClass = iDesiredClass;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// for non-local players, show the current class
|
|
iClass = tf_PR->GetPlayerClass( playerIndex );
|
|
}
|
|
|
|
if( iClass >= TF_FIRST_NORMAL_CLASS && iClass <= TF_LAST_NORMAL_CLASS )
|
|
{
|
|
pKeyValues->SetString( "class", g_aPlayerClassNames[iClass] );
|
|
}
|
|
else
|
|
{
|
|
pKeyValues->SetString( "class", "" );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
C_TFPlayer *pPlayerOther = ToTFPlayer( UTIL_PlayerByIndex( playerIndex ) );
|
|
|
|
#ifdef _X360
|
|
bool bUseTruncatedNames = true;
|
|
#else
|
|
bool bUseTruncatedNames = false;
|
|
#endif
|
|
|
|
if ( pPlayerOther && pPlayerOther->m_Shared.IsPlayerDominated( pLocalPlayer->entindex() ) )
|
|
{
|
|
// if local player is dominated by this player, show a nemesis icon
|
|
pKeyValues->SetInt( "nemesis", m_iImageNemesis );
|
|
pKeyValues->SetString( "class", bUseTruncatedNames ? "#TF_Nemesis_lodef" : "#TF_Nemesis" );
|
|
}
|
|
else if ( pLocalPlayer->m_Shared.IsPlayerDominated( playerIndex) )
|
|
{
|
|
// if this player is dominated by the local player, show the domination icon
|
|
pKeyValues->SetInt( "nemesis", m_iImageDominated );
|
|
pKeyValues->SetString( "class", bUseTruncatedNames ? "#TF_Dominated_lodef" : "#TF_Dominated" );
|
|
}
|
|
}
|
|
|
|
// display whether player is alive or dead (all players see this for all other players on both teams)
|
|
pKeyValues->SetInt( "status", tf_PR->IsAlive( playerIndex ) ? 0 : m_iImageDead );
|
|
|
|
if ( g_PR->GetPing( playerIndex ) < 1 )
|
|
{
|
|
if ( g_PR->IsFakePlayer( playerIndex ) )
|
|
{
|
|
pKeyValues->SetString( "ping", "#TF_Scoreboard_Bot" );
|
|
}
|
|
else
|
|
{
|
|
pKeyValues->SetString( "ping", "" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pKeyValues->SetInt( "ping", g_PR->GetPing( playerIndex ) );
|
|
}
|
|
|
|
|
|
UpdatePlayerAvatar( playerIndex, pKeyValues );
|
|
|
|
int itemID = pPlayerList->AddItem( 0, pKeyValues );
|
|
Color clr = g_PR->GetTeamColor( g_PR->GetTeam( playerIndex ) );
|
|
pPlayerList->SetItemFgColor( itemID, clr );
|
|
|
|
if ( iSelectedPlayerIndex == playerIndex )
|
|
{
|
|
bMadeSelection = true;
|
|
pPlayerList->SetSelectedItem( itemID );
|
|
}
|
|
|
|
pKeyValues->deleteThis();
|
|
}
|
|
}
|
|
|
|
// If we're on spectator, find a default selection
|
|
if ( !bMadeSelection )
|
|
{
|
|
if ( m_pPlayerListBlue->GetItemCount() > 0 )
|
|
{
|
|
m_pPlayerListBlue->SetSelectedItem( 0 );
|
|
}
|
|
else if ( m_pPlayerListRed->GetItemCount() > 0 )
|
|
{
|
|
m_pPlayerListRed->SetSelectedItem( 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates the spectator list
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::UpdateSpectatorList()
|
|
{
|
|
char szSpectatorList[512] = "" ;
|
|
int nSpectators = 0;
|
|
for( int playerIndex = 1 ; playerIndex <= MAX_PLAYERS; playerIndex++ )
|
|
{
|
|
if ( ShouldShowAsSpectator( playerIndex ) )
|
|
{
|
|
if ( nSpectators > 0 )
|
|
{
|
|
Q_strncat( szSpectatorList, ", ", ARRAYSIZE( szSpectatorList ) );
|
|
}
|
|
|
|
Q_strncat( szSpectatorList, g_PR->GetPlayerName( playerIndex ), ARRAYSIZE( szSpectatorList ) );
|
|
nSpectators++;
|
|
}
|
|
}
|
|
|
|
wchar_t wzSpectators[512] = L"";
|
|
if ( nSpectators > 0 )
|
|
{
|
|
const char *pchFormat = ( 1 == nSpectators ? "#ScoreBoard_Spectator" : "#ScoreBoard_Spectators" );
|
|
|
|
wchar_t wzSpectatorCount[16];
|
|
wchar_t wzSpectatorList[1024];
|
|
_snwprintf( wzSpectatorCount, ARRAYSIZE( wzSpectatorCount ), L"%i", nSpectators );
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( szSpectatorList, wzSpectatorList, sizeof( wzSpectatorList ) );
|
|
g_pVGuiLocalize->ConstructString( wzSpectators, sizeof(wzSpectators), g_pVGuiLocalize->Find( pchFormat), 2, wzSpectatorCount, wzSpectatorList );
|
|
}
|
|
SetDialogVariable( "spectators", wzSpectators );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Updates details about a player
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::UpdatePlayerDetails()
|
|
{
|
|
ClearPlayerDetails();
|
|
|
|
C_TF_PlayerResource *tf_PR = dynamic_cast<C_TF_PlayerResource *>( g_PR );
|
|
if ( !tf_PR )
|
|
return;
|
|
|
|
C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
|
|
if ( !pLocalPlayer )
|
|
return;
|
|
int playerIndex = pLocalPlayer->entindex();
|
|
|
|
// Make sure the selected player is still connected.
|
|
if ( !tf_PR->IsConnected( playerIndex ) )
|
|
return;
|
|
|
|
if ( engine->IsHLTV() )
|
|
{
|
|
SetDialogVariable( "playername", tf_PR->GetPlayerName( playerIndex ) );
|
|
return;
|
|
}
|
|
|
|
RoundStats_t &roundStats = GetStatPanel()->GetRoundStatsCurrentGame();
|
|
|
|
SetDialogVariable( "kills", tf_PR->GetPlayerScore( playerIndex ) );
|
|
SetDialogVariable( "deaths", tf_PR->GetDeaths( playerIndex ) );
|
|
SetDialogVariable( "assists", roundStats.m_iStat[TFSTAT_KILLASSISTS] );
|
|
SetDialogVariable( "destruction", roundStats.m_iStat[TFSTAT_BUILDINGSDESTROYED] );
|
|
SetDialogVariable( "captures", roundStats.m_iStat[TFSTAT_CAPTURES] );
|
|
SetDialogVariable( "defenses", roundStats.m_iStat[TFSTAT_DEFENSES] );
|
|
SetDialogVariable( "dominations", roundStats.m_iStat[TFSTAT_DOMINATIONS] );
|
|
SetDialogVariable( "revenge", roundStats.m_iStat[TFSTAT_REVENGE] );
|
|
SetDialogVariable( "healing", roundStats.m_iStat[TFSTAT_HEALING] );
|
|
SetDialogVariable( "invulns", roundStats.m_iStat[TFSTAT_INVULNS] );
|
|
SetDialogVariable( "teleports", roundStats.m_iStat[TFSTAT_TELEPORTS] );
|
|
SetDialogVariable( "headshots", roundStats.m_iStat[TFSTAT_HEADSHOTS] );
|
|
SetDialogVariable( "backstabs", roundStats.m_iStat[TFSTAT_BACKSTABS] );
|
|
SetDialogVariable( "playername", tf_PR->GetPlayerName( playerIndex ) );
|
|
SetDialogVariable( "playerscore", GetPointsString( tf_PR->GetTotalScore( playerIndex ) ) );
|
|
Color clr = g_PR->GetTeamColor( g_PR->GetTeam( playerIndex ) );
|
|
m_pLabelPlayerName->SetFgColor( clr );
|
|
m_pImagePanelHorizLine->SetFillColor( clr );
|
|
|
|
int iClass = pLocalPlayer->m_Shared.GetDesiredPlayerClassIndex();
|
|
int iTeam = pLocalPlayer->GetTeamNumber();
|
|
if ( ( iTeam >= FIRST_GAME_TEAM ) && ( iClass >= TF_FIRST_NORMAL_CLASS ) && ( iClass <= TF_LAST_NORMAL_CLASS ) )
|
|
{
|
|
m_pClassImage->SetClass( iTeam, iClass, 0 );
|
|
m_pClassImage->SetVisible( true );
|
|
}
|
|
else
|
|
{
|
|
m_pClassImage->SetVisible( false );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Clears score details
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::ClearPlayerDetails()
|
|
{
|
|
m_pClassImage->SetVisible( false );
|
|
|
|
// HLTV has no game stats
|
|
bool bVisible = !engine->IsHLTV();
|
|
|
|
SetDialogVariable( "kills", "" );
|
|
SetControlVisible( "KillsLabel", bVisible );
|
|
|
|
SetDialogVariable( "deaths", "" );
|
|
SetControlVisible( "DeathsLabel", bVisible );
|
|
|
|
SetDialogVariable( "captures", "" );
|
|
SetControlVisible( "CapturesLabel", bVisible );
|
|
|
|
SetDialogVariable( "defenses", "" );
|
|
SetControlVisible( "DefensesLabel", bVisible );
|
|
|
|
SetDialogVariable( "dominations", "" );
|
|
SetControlVisible( "DominationLabel", bVisible );
|
|
|
|
SetDialogVariable( "revenge", "" );
|
|
SetControlVisible( "RevengeLabel", bVisible );
|
|
|
|
SetDialogVariable( "assists", "" );
|
|
SetControlVisible( "AssistsLabel", bVisible );
|
|
|
|
SetDialogVariable( "destruction", "" );
|
|
SetControlVisible( "DestructionLabel", bVisible );
|
|
|
|
SetDialogVariable( "healing", "" );
|
|
SetControlVisible( "HealingLabel", bVisible );
|
|
|
|
SetDialogVariable( "invulns", "" );
|
|
SetControlVisible( "InvulnLabel", bVisible );
|
|
|
|
SetDialogVariable( "teleports", "" );
|
|
SetControlVisible( "TeleportsLabel", bVisible );
|
|
|
|
SetDialogVariable( "headshots", "" );
|
|
SetControlVisible( "HeadshotsLabel", bVisible );
|
|
|
|
SetDialogVariable( "backstabs", "" );
|
|
SetControlVisible( "BackstabsLabel", bVisible );
|
|
|
|
SetDialogVariable( "playername", "" );
|
|
|
|
SetDialogVariable( "playerscore", "" );
|
|
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used for sorting players
|
|
//-----------------------------------------------------------------------------
|
|
bool CTFClientScoreBoardDialog::TFPlayerSortFunc( vgui::SectionedListPanel *list, int itemID1, int itemID2 )
|
|
{
|
|
KeyValues *it1 = list->GetItemData(itemID1);
|
|
KeyValues *it2 = list->GetItemData(itemID2);
|
|
Assert(it1 && it2);
|
|
|
|
// first compare score
|
|
int v1 = it1->GetInt("score");
|
|
int v2 = it2->GetInt("score");
|
|
if (v1 > v2)
|
|
return true;
|
|
else if (v1 < v2)
|
|
return false;
|
|
|
|
// if score is the same, use player index to get deterministic sort
|
|
int iPlayerIndex1 = it1->GetInt( "playerIndex" );
|
|
int iPlayerIndex2 = it2->GetInt( "playerIndex" );
|
|
return ( iPlayerIndex1 > iPlayerIndex2 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns a localized string of form "1 point", "2 points", etc for specified # of points
|
|
//-----------------------------------------------------------------------------
|
|
const wchar_t *GetPointsString( int iPoints )
|
|
{
|
|
wchar_t wzScoreVal[128];
|
|
static wchar_t wzScore[128];
|
|
_snwprintf( wzScoreVal, ARRAYSIZE( wzScoreVal ), L"%i", iPoints );
|
|
if ( 1 == iPoints )
|
|
{
|
|
g_pVGuiLocalize->ConstructString( wzScore, sizeof(wzScore), g_pVGuiLocalize->Find( "#TF_ScoreBoard_Point" ), 1, wzScoreVal );
|
|
}
|
|
else
|
|
{
|
|
g_pVGuiLocalize->ConstructString( wzScore, sizeof(wzScore), g_pVGuiLocalize->Find( "#TF_ScoreBoard_Points" ), 1, wzScoreVal );
|
|
}
|
|
return wzScore;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns whether the specified player index is a spectator
|
|
//-----------------------------------------------------------------------------
|
|
bool CTFClientScoreBoardDialog::ShouldShowAsSpectator( int iPlayerIndex )
|
|
{
|
|
C_TF_PlayerResource *tf_PR = dynamic_cast<C_TF_PlayerResource *>( g_PR );
|
|
if ( !tf_PR )
|
|
return false;
|
|
|
|
// see if player is connected
|
|
if ( tf_PR->IsConnected( iPlayerIndex ) )
|
|
{
|
|
// either spectating or unassigned team should show in spectator list
|
|
int iTeam = tf_PR->GetTeam( iPlayerIndex );
|
|
if ( TEAM_SPECTATOR == iTeam || TEAM_UNASSIGNED == iTeam )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Event handler
|
|
//-----------------------------------------------------------------------------
|
|
void CTFClientScoreBoardDialog::FireGameEvent( IGameEvent *event )
|
|
{
|
|
const char *type = event->GetName();
|
|
|
|
if ( 0 == Q_strcmp( type, "server_spawn" ) )
|
|
{
|
|
// set server name in scoreboard
|
|
const char *hostname = event->GetString( "hostname" );
|
|
wchar_t wzHostName[256];
|
|
wchar_t wzServerLabel[256];
|
|
g_pVGuiLocalize->ConvertANSIToUnicode( hostname, wzHostName, sizeof( wzHostName ) );
|
|
g_pVGuiLocalize->ConstructString( wzServerLabel, sizeof(wzServerLabel), g_pVGuiLocalize->Find( "#Scoreboard_Server" ), 1, wzHostName );
|
|
SetDialogVariable( "server", wzServerLabel );
|
|
}
|
|
|
|
if( IsVisible() )
|
|
{
|
|
Update();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
SectionedListPanel *CTFClientScoreBoardDialog::GetSelectedPlayerList( void )
|
|
{
|
|
SectionedListPanel *pList = NULL;
|
|
|
|
// navigation
|
|
if ( m_pPlayerListBlue->GetSelectedItem() >= 0 )
|
|
{
|
|
pList = m_pPlayerListBlue;
|
|
}
|
|
else if ( m_pPlayerListRed->GetSelectedItem() >= 0 )
|
|
{
|
|
pList = m_pPlayerListRed;
|
|
}
|
|
|
|
return pList;
|
|
}
|
|
|
|
#if defined ( _X360 )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Event handler
|
|
//-----------------------------------------------------------------------------
|
|
int CTFClientScoreBoardDialog::HudElementKeyInput( int down, ButtonCode_t keynum, const char *pszCurrentBinding )
|
|
{
|
|
if ( !IsVisible() )
|
|
return 1;
|
|
|
|
if ( !down )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
SectionedListPanel *pList = GetSelectedPlayerList();
|
|
|
|
switch( keynum )
|
|
{
|
|
case KEY_XBUTTON_UP:
|
|
{
|
|
if ( pList )
|
|
{
|
|
pList->MoveSelectionUp();
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_DOWN:
|
|
{
|
|
if ( pList )
|
|
{
|
|
pList->MoveSelectionDown();
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_RIGHT:
|
|
{
|
|
if ( m_pPlayerListRed->GetItemCount() == 0 )
|
|
return 0;
|
|
|
|
// move to the red list
|
|
|
|
// get the row we're in now
|
|
int iSelectedBlueItem = m_pPlayerListBlue->GetSelectedItem();
|
|
|
|
m_pPlayerListBlue->ClearSelection();
|
|
|
|
if ( iSelectedBlueItem >= 0 )
|
|
{
|
|
int row = m_pPlayerListBlue->GetRowFromItemID( iSelectedBlueItem );
|
|
|
|
if ( row >= 0 )
|
|
{
|
|
int iNewItem = m_pPlayerListRed->GetItemIDFromRow( row );
|
|
|
|
if ( iNewItem >= 0 )
|
|
{
|
|
m_pPlayerListRed->SetSelectedItem( iNewItem );
|
|
}
|
|
else
|
|
{
|
|
// we have fewer items. Select the last one
|
|
int iLastRow = m_pPlayerListRed->GetItemCount()-1;
|
|
|
|
iNewItem = m_pPlayerListRed->GetItemIDFromRow( iLastRow );
|
|
|
|
if ( iNewItem >= 0 )
|
|
{
|
|
m_pPlayerListRed->SetSelectedItem( iNewItem );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_LEFT:
|
|
{
|
|
if ( m_pPlayerListBlue->GetItemCount() == 0 )
|
|
return 0;
|
|
|
|
// move to the blue list
|
|
|
|
// get the row we're in now
|
|
int iSelectedRedItem = m_pPlayerListRed->GetSelectedItem();
|
|
|
|
if ( iSelectedRedItem < 0 )
|
|
iSelectedRedItem = 0;
|
|
|
|
m_pPlayerListRed->ClearSelection();
|
|
|
|
if ( iSelectedRedItem >= 0 )
|
|
{
|
|
int row = m_pPlayerListRed->GetRowFromItemID( iSelectedRedItem );
|
|
|
|
if ( row >= 0 )
|
|
{
|
|
int iNewItem = m_pPlayerListBlue->GetItemIDFromRow( row );
|
|
|
|
if ( iNewItem >= 0 )
|
|
{
|
|
m_pPlayerListBlue->SetSelectedItem( iNewItem );
|
|
}
|
|
else
|
|
{
|
|
// we have fewer items. Select the last one
|
|
int iLastRow = m_pPlayerListBlue->GetItemCount()-1;
|
|
|
|
iNewItem = m_pPlayerListBlue->GetItemIDFromRow( iLastRow );
|
|
|
|
if ( iNewItem >= 0 )
|
|
{
|
|
m_pPlayerListBlue->SetSelectedItem( iNewItem );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_B:
|
|
{
|
|
ShowPanel( false );
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_A: // Show GamerCard for the selected player
|
|
{
|
|
if ( pList )
|
|
{
|
|
int iSelectedItem = pList->GetSelectedItem();
|
|
|
|
if ( iSelectedItem >= 0 )
|
|
{
|
|
KeyValues *pInfo = pList->GetItemData( iSelectedItem );
|
|
|
|
DevMsg( 1, "XShowGamerCardUI for player '%s'\n", pInfo->GetString( "name" ) );
|
|
|
|
uint64 xuid = matchmaking->PlayerIdToXuid( pInfo->GetInt( "playerIndex" ) );
|
|
XShowGamerCardUI( XBX_GetPrimaryUserId(), xuid );
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case KEY_XBUTTON_X: // Show player review for the selected player
|
|
{
|
|
if ( pList )
|
|
{
|
|
int iSelectedItem = pList->GetSelectedItem();
|
|
|
|
if ( iSelectedItem >= 0 )
|
|
{
|
|
KeyValues *pInfo = pList->GetItemData( iSelectedItem );
|
|
|
|
DevMsg( 1, "XShowPlayerReviewUI for player '%s'\n", pInfo->GetString( "name" ) );
|
|
|
|
uint64 xuid = matchmaking->PlayerIdToXuid( pInfo->GetInt( "playerIndex" ) );
|
|
XShowPlayerReviewUI( XBX_GetPrimaryUserId(), xuid );
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
#endif //_X360
|