summaryrefslogtreecommitdiff
path: root/game/client/game_controls/ClientScoreBoardDialog.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/game_controls/ClientScoreBoardDialog.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/game_controls/ClientScoreBoardDialog.cpp')
-rw-r--r--game/client/game_controls/ClientScoreBoardDialog.cpp580
1 files changed, 580 insertions, 0 deletions
diff --git a/game/client/game_controls/ClientScoreBoardDialog.cpp b/game/client/game_controls/ClientScoreBoardDialog.cpp
new file mode 100644
index 0000000..29865b0
--- /dev/null
+++ b/game/client/game_controls/ClientScoreBoardDialog.cpp
@@ -0,0 +1,580 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "cbase.h"
+#include <stdio.h>
+
+#include <cdll_client_int.h>
+#include <cdll_util.h>
+#include <globalvars_base.h>
+#include <igameresources.h>
+#include "IGameUIFuncs.h" // for key bindings
+#include "inputsystem/iinputsystem.h"
+#include "clientscoreboarddialog.h"
+#include <voice_status.h>
+
+#include <vgui/IScheme.h>
+#include <vgui/ILocalize.h>
+#include <vgui/ISurface.h>
+#include <vgui/IVGui.h>
+#include <vstdlib/IKeyValuesSystem.h>
+
+#include <KeyValues.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/SectionedListPanel.h>
+
+#include <game/client/iviewport.h>
+#include <igameresources.h>
+
+#include "vgui_avatarimage.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+bool AvatarIndexLessFunc( const int &lhs, const int &rhs )
+{
+ return lhs < rhs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CClientScoreBoardDialog::CClientScoreBoardDialog(IViewPort *pViewPort) : EditablePanel( NULL, PANEL_SCOREBOARD )
+{
+ m_iPlayerIndexSymbol = KeyValuesSystem()->GetSymbolForString("playerIndex");
+ m_nCloseKey = BUTTON_CODE_INVALID;
+
+ //memset(s_VoiceImage, 0x0, sizeof( s_VoiceImage ));
+ TrackerImage = 0;
+ m_pViewPort = pViewPort;
+
+ // initialize dialog
+ SetProportional(true);
+ SetKeyBoardInputEnabled(false);
+ SetMouseInputEnabled(false);
+
+ // set the scheme before any child control is created
+ SetScheme("ClientScheme");
+
+ m_pPlayerList = new SectionedListPanel(this, "PlayerList");
+ m_pPlayerList->SetVerticalScrollbar(false);
+
+ LoadControlSettings("Resource/UI/ScoreBoard.res");
+ m_iDesiredHeight = GetTall();
+ m_pPlayerList->SetVisible( false ); // hide this until we load the images in applyschemesettings
+
+ m_HLTVSpectators = 0;
+ m_ReplaySpectators = 0;
+
+ // update scoreboard instantly if on of these events occure
+ ListenForGameEvent( "hltv_status" );
+ ListenForGameEvent( "server_spawn" );
+
+ m_pImageList = NULL;
+
+ m_mapAvatarsToImageList.SetLessFunc( DefLessFunc( CSteamID ) );
+ m_mapAvatarsToImageList.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CClientScoreBoardDialog::~CClientScoreBoardDialog()
+{
+ if ( NULL != m_pImageList )
+ {
+ delete m_pImageList;
+ m_pImageList = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Call every frame
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::OnThink()
+{
+ BaseClass::OnThink();
+
+ // NOTE: this is necessary because of the way input works.
+ // If a key down message is sent to vgui, then it will get the key up message
+ // Sometimes the scoreboard is activated by other vgui menus,
+ // sometimes by console commands. In the case where it's activated by
+ // other vgui menus, we lose the key up message because this panel
+ // doesn't accept keyboard input. It *can't* accept keyboard input
+ // because another feature of the dialog is that if it's triggered
+ // from within the game, you should be able to still run around while
+ // the scoreboard is up. That feature is impossible if this panel accepts input.
+ // because if a vgui panel is up that accepts input, it prevents the engine from
+ // receiving that input. So, I'm stuck with a polling solution.
+ //
+ // Close key is set to non-invalid when something other than a keybind
+ // brings the scoreboard up, and it's set to invalid as soon as the
+ // dialog becomes hidden.
+ if ( m_nCloseKey != BUTTON_CODE_INVALID )
+ {
+ if ( !g_pInputSystem->IsButtonDown( m_nCloseKey ) )
+ {
+ m_nCloseKey = BUTTON_CODE_INVALID;
+ gViewPortInterface->ShowPanel( PANEL_SCOREBOARD, false );
+ GetClientVoiceMgr()->StopSquelchMode();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called by vgui panels that activate the client scoreboard
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::OnPollHideCode( int code )
+{
+ m_nCloseKey = (ButtonCode_t)code;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clears everything in the scoreboard and all it's state
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::Reset()
+{
+ // clear
+ m_pPlayerList->DeleteAllItems();
+ m_pPlayerList->RemoveAllSections();
+
+ m_iSectionId = 0;
+ m_fNextUpdateTime = 0;
+ // add all the sections
+ InitScoreboardSections();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds all the team sections to the scoreboard
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::InitScoreboardSections()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets up screen
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ if ( m_pImageList )
+ delete m_pImageList;
+ m_pImageList = new ImageList( false );
+
+ m_mapAvatarsToImageList.RemoveAll();
+
+ PostApplySchemeSettings( pScheme );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Does dialog-specific customization after applying scheme settings.
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::PostApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ // resize the images to our resolution
+ for (int i = 0; i < m_pImageList->GetImageCount(); i++ )
+ {
+ int wide, tall;
+ m_pImageList->GetImage(i)->GetSize(wide, tall);
+ m_pImageList->GetImage(i)->SetSize(scheme()->GetProportionalScaledValueEx( GetScheme(),wide), scheme()->GetProportionalScaledValueEx( GetScheme(),tall));
+ }
+
+ m_pPlayerList->SetImageList( m_pImageList, false );
+ m_pPlayerList->SetVisible( true );
+
+ // light up scoreboard a bit
+ SetBgColor( Color( 0,0,0,0) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::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 );
+ }
+
+ if ( !bShow )
+ {
+ m_nCloseKey = BUTTON_CODE_INVALID;
+ }
+
+ if ( BaseClass::IsVisible() == bShow )
+ return;
+
+ if ( bShow )
+ {
+ Reset();
+ Update();
+ SetVisible( true );
+ MoveToFront();
+ }
+ else
+ {
+ BaseClass::SetVisible( false );
+ SetMouseInputEnabled( false );
+ SetKeyBoardInputEnabled( false );
+ }
+}
+
+void CClientScoreBoardDialog::FireGameEvent( IGameEvent *event )
+{
+ const char * type = event->GetName();
+
+ if ( Q_strcmp(type, "hltv_status") == 0 )
+ {
+ // spectators = clients - proxies
+ m_HLTVSpectators = event->GetInt( "clients" );
+ m_HLTVSpectators -= event->GetInt( "proxies" );
+ }
+ else if ( Q_strcmp(type, "server_spawn") == 0 )
+ {
+ // We'll post the message ourselves instead of using SetControlString()
+ // so we don't try to translate the hostname.
+ const char *hostname = event->GetString( "hostname" );
+ Panel *control = FindChildByName( "ServerName" );
+ if ( control )
+ {
+ PostMessage( control, new KeyValues( "SetText", "text", hostname ) );
+ control->MoveToFront();
+ }
+ }
+
+ if( IsVisible() )
+ Update();
+
+}
+
+bool CClientScoreBoardDialog::NeedsUpdate( void )
+{
+ return (m_fNextUpdateTime < gpGlobals->curtime);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate the internal scoreboard data
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::Update( void )
+{
+ // Set the title
+
+ // Reset();
+ m_pPlayerList->DeleteAllItems();
+
+ FillScoreBoard();
+
+ // grow the scoreboard to fit all the players
+ int wide, tall;
+ m_pPlayerList->GetContentSize(wide, tall);
+ tall += GetAdditionalHeight();
+ wide = GetWide();
+ if (m_iDesiredHeight < tall)
+ {
+ SetSize(wide, tall);
+ m_pPlayerList->SetSize(wide, tall);
+ }
+ else
+ {
+ SetSize(wide, m_iDesiredHeight);
+ m_pPlayerList->SetSize(wide, m_iDesiredHeight);
+ }
+
+ MoveToCenterOfScreen();
+
+ // update every second
+ m_fNextUpdateTime = gpGlobals->curtime + 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sort all the teams
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::UpdateTeamInfo()
+{
+// TODO: work out a sorting algorithm for team display for TF2
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::UpdatePlayerInfo()
+{
+ m_iSectionId = 0; // 0'th row is a header
+ int selectedRow = -1;
+
+ // walk all the players and make sure they're in the scoreboard
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ IGameResources *gr = GameResources();
+
+ if ( gr && gr->IsConnected( i ) )
+ {
+ // add the player to the list
+ KeyValues *playerData = new KeyValues("data");
+ GetPlayerScoreInfo( i, playerData );
+ UpdatePlayerAvatar( i, playerData );
+
+ const char *oldName = playerData->GetString("name","");
+ char newName[MAX_PLAYER_NAME_LENGTH];
+
+ UTIL_MakeSafeName( oldName, newName, MAX_PLAYER_NAME_LENGTH );
+
+ playerData->SetString("name", newName);
+
+ int itemID = FindItemIDForPlayerIndex( i );
+ int sectionID = gr->GetTeam( i );
+
+ if ( gr->IsLocalPlayer( i ) )
+ {
+ selectedRow = itemID;
+ }
+ if (itemID == -1)
+ {
+ // add a new row
+ itemID = m_pPlayerList->AddItem( sectionID, playerData );
+ }
+ else
+ {
+ // modify the current row
+ m_pPlayerList->ModifyItem( itemID, sectionID, playerData );
+ }
+
+ // set the row color based on the players team
+ m_pPlayerList->SetItemFgColor( itemID, gr->GetTeamColor( sectionID ) );
+
+ playerData->deleteThis();
+ }
+ else
+ {
+ // remove the player
+ int itemID = FindItemIDForPlayerIndex( i );
+ if (itemID != -1)
+ {
+ m_pPlayerList->RemoveItem(itemID);
+ }
+ }
+ }
+
+ if ( selectedRow != -1 )
+ {
+ m_pPlayerList->SetSelectedItem(selectedRow);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: adds the top header of the scoreboars
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::AddHeader()
+{
+ // add the top header
+ m_pPlayerList->AddSection(m_iSectionId, "");
+ m_pPlayerList->SetSectionAlwaysVisible(m_iSectionId);
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#PlayerName", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "#PlayerScore", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "#PlayerDeath", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "#PlayerPing", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a new section to the scoreboard (i.e the team header)
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::AddSection(int teamType, int teamNumber)
+{
+ if ( teamType == TYPE_TEAM )
+ {
+ IGameResources *gr = GameResources();
+
+ if ( !gr )
+ return;
+
+ // setup the team name
+ wchar_t *teamName = g_pVGuiLocalize->Find( gr->GetTeamName(teamNumber) );
+ wchar_t name[64];
+ wchar_t string1[1024];
+
+ if (!teamName)
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode(gr->GetTeamName(teamNumber), name, sizeof(name));
+ teamName = name;
+ }
+
+ g_pVGuiLocalize->ConstructString_safe( string1, g_pVGuiLocalize->Find("#Player"), 2, teamName );
+
+ m_pPlayerList->AddSection(m_iSectionId, "", StaticPlayerSortFunc);
+
+ // Avatars are always displayed at 32x32 regardless of resolution
+ if ( ShowAvatars() )
+ {
+ m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth );
+ }
+
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "name", string1, 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "deaths", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),DEATH_WIDTH) );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "ping", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),PING_WIDTH) );
+ }
+ else if ( teamType == TYPE_SPECTATORS )
+ {
+ m_pPlayerList->AddSection(m_iSectionId, "");
+
+ // Avatars are always displayed at 32x32 regardless of resolution
+ if ( ShowAvatars() )
+ {
+ m_pPlayerList->AddColumnToSection( m_iSectionId, "avatar", "", SectionedListPanel::COLUMN_IMAGE | SectionedListPanel::COLUMN_RIGHT, m_iAvatarWidth );
+ }
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "name", "#Spectators", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),NAME_WIDTH) - m_iAvatarWidth );
+ m_pPlayerList->AddColumnToSection(m_iSectionId, "frags", "", 0, scheme()->GetProportionalScaledValueEx( GetScheme(),SCORE_WIDTH) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used for sorting players
+//-----------------------------------------------------------------------------
+bool CClientScoreBoardDialog::StaticPlayerSortFunc(vgui::SectionedListPanel *list, int itemID1, int itemID2)
+{
+ KeyValues *it1 = list->GetItemData(itemID1);
+ KeyValues *it2 = list->GetItemData(itemID2);
+ Assert(it1 && it2);
+
+ // first compare frags
+ int v1 = it1->GetInt("frags");
+ int v2 = it2->GetInt("frags");
+ if (v1 > v2)
+ return true;
+ else if (v1 < v2)
+ return false;
+
+ // next compare deaths
+ v1 = it1->GetInt("deaths");
+ v2 = it2->GetInt("deaths");
+ if (v1 > v2)
+ return false;
+ else if (v1 < v2)
+ return true;
+
+ // the same, so compare itemID's (as a sentinel value to get deterministic sorts)
+ return itemID1 < itemID2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds a new row to the scoreboard, from the playerinfo structure
+//-----------------------------------------------------------------------------
+bool CClientScoreBoardDialog::GetPlayerScoreInfo(int playerIndex, KeyValues *kv)
+{
+ IGameResources *gr = GameResources();
+
+ if (!gr )
+ return false;
+
+ kv->SetInt("deaths", gr->GetDeaths( playerIndex ) );
+ kv->SetInt("frags", gr->GetFrags( playerIndex ) );
+ kv->SetInt("ping", gr->GetPing( playerIndex ) ) ;
+ kv->SetString("name", gr->GetPlayerName( playerIndex ) );
+ kv->SetInt("playerIndex", playerIndex);
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::UpdatePlayerAvatar( int playerIndex, KeyValues *kv )
+{
+ // Update their avatar
+ if ( kv && ShowAvatars() && steamapicontext->SteamFriends() && steamapicontext->SteamUtils() )
+ {
+ player_info_t pi;
+ if ( engine->GetPlayerInfo( playerIndex, &pi ) )
+ {
+ if ( pi.friendsID )
+ {
+ CSteamID steamIDForPlayer( pi.friendsID, 1, GetUniverse(), k_EAccountTypeIndividual );
+
+ // See if we already have that avatar in our list
+ int iMapIndex = m_mapAvatarsToImageList.Find( steamIDForPlayer );
+ int iImageIndex;
+ if ( iMapIndex == m_mapAvatarsToImageList.InvalidIndex() )
+ {
+ CAvatarImage *pImage = new CAvatarImage();
+ pImage->SetAvatarSteamID( steamIDForPlayer );
+ pImage->SetAvatarSize( 32, 32 ); // Deliberately non scaling
+ iImageIndex = m_pImageList->AddImage( pImage );
+
+ m_mapAvatarsToImageList.Insert( steamIDForPlayer, iImageIndex );
+ }
+ else
+ {
+ iImageIndex = m_mapAvatarsToImageList[ iMapIndex ];
+ }
+
+ kv->SetInt( "avatar", iImageIndex );
+
+ CAvatarImage *pAvIm = (CAvatarImage *)m_pImageList->GetImage( iImageIndex );
+ pAvIm->UpdateFriendStatus();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: reload the player list on the scoreboard
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::FillScoreBoard()
+{
+ // update totals information
+ UpdateTeamInfo();
+
+ // update player info
+ UpdatePlayerInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: searches for the player in the scoreboard
+//-----------------------------------------------------------------------------
+int CClientScoreBoardDialog::FindItemIDForPlayerIndex(int playerIndex)
+{
+ for (int i = 0; i <= m_pPlayerList->GetHighestItemID(); i++)
+ {
+ if (m_pPlayerList->IsItemIDValid(i))
+ {
+ KeyValues *kv = m_pPlayerList->GetItemData(i);
+ kv = kv->FindKey(m_iPlayerIndexSymbol);
+ if (kv && kv->GetInt() == playerIndex)
+ return i;
+ }
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the text of a control by name
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::MoveLabelToFront(const char *textEntryName)
+{
+ Label *entry = dynamic_cast<Label *>(FindChildByName(textEntryName));
+ if (entry)
+ {
+ entry->MoveToFront();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Center the dialog on the screen. (vgui has this method on
+// Frame, but we're an EditablePanel, need to roll our own.)
+//-----------------------------------------------------------------------------
+void CClientScoreBoardDialog::MoveToCenterOfScreen()
+{
+ int wx, wy, ww, wt;
+ surface()->GetWorkspaceBounds(wx, wy, ww, wt);
+ SetPos((ww - GetWide()) / 2, (wt - GetTall()) / 2);
+}