//=========== (C) Copyright 1996-2002 Valve, L.L.C. All rights reserved. =========== // // The copyright to the contents herein is the property of Valve, L.L.C. // The contents may be used and/or copied only with the written permission of // Valve, L.L.C., or in accordance with the terms and conditions stipulated in // the agreement/contract under which the contents have been supplied. // // Purpose: VGUI scoreboard // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //============================================================================= #include #include "hud.h" #include "cl_util.h" #include "const.h" #include "entity_state.h" #include "cl_entity.h" #include "vgui_TeamFortressViewport.h" #include "vgui_ScorePanel.h" #include "vgui_helpers.h" #include "vgui_loadtga.h" #include "voice_status.h" #include "vgui_SpectatorPanel.h" int HUD_IsGame( const char *game ); int EV_TFC_IsAllyTeam( int iTeam1, int iTeam2 ); // Scoreboard dimensions #define SBOARD_TITLE_SIZE_Y YRES(22) #define X_BORDER XRES(4) // Column sizes class SBColumnInfo { public: const char *m_pTitle; // If null, ignore, if starts with #, it's localized, otherwise use the string directly. int m_Width; // Based on 640 width. Scaled to fit other resolutions. Label::Alignment m_Alignment; }; // grid size is marked out for 640x480 screen SBColumnInfo g_ColumnInfo[NUM_COLUMNS] = { {NULL, 24, Label::a_east}, // tracker column {NULL, 140, Label::a_east}, // name {NULL, 56, Label::a_east}, // class {"#SCORE", 40, Label::a_east}, {"#DEATHS", 46, Label::a_east}, {"#LATENCY", 46, Label::a_east}, {"#VOICE", 40, Label::a_east}, {NULL, 2, Label::a_east}, // blank column to take up the slack }; #define TEAM_NO 0 #define TEAM_YES 1 #define TEAM_SPECTATORS 2 #define TEAM_BLANK 3 //----------------------------------------------------------------------------- // ScorePanel::HitTestPanel. //----------------------------------------------------------------------------- void ScorePanel::HitTestPanel::internalMousePressed( MouseCode code ) { for( int i = 0; i < _inputSignalDar.getCount(); i++ ) { _inputSignalDar[i]->mousePressed( code, this ); } } //----------------------------------------------------------------------------- // Purpose: Create the ScoreBoard panel //----------------------------------------------------------------------------- ScorePanel::ScorePanel( int x, int y, int wide, int tall ) : Panel( x, y, wide, tall ) { CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle( "Scoreboard Title Text" ); SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle( "Scoreboard Small Text" ); Font *tfont = pSchemes->getFont( hTitleScheme ); Font *smallfont = pSchemes->getFont( hSmallScheme ); setBgColor( 0, 0, 0, 96 ); m_pCurrentHighlightLabel = NULL; m_iHighlightRow = -1; //m_pTrackerIcon = vgui_LoadTGANoInvertAlpha("gfx/vgui/640_scoreboardtracker.tga"); // Initialize the top title. m_TitleLabel.setFont( tfont ); m_TitleLabel.setText( "" ); m_TitleLabel.setBgColor( 0, 0, 0, 255 ); m_TitleLabel.setFgColor( Scheme::sc_primary1 ); m_TitleLabel.setContentAlignment( vgui::Label::a_west ); LineBorder *border = new LineBorder( Color( 60, 60, 60, 128 ) ); setBorder( border ); setPaintBorderEnabled( true ); int xpos = g_ColumnInfo[0].m_Width + 3; if( ScreenWidth >= 640 ) { // only expand column size for res greater than 640 xpos = XRES( xpos ); } m_TitleLabel.setBounds( xpos, 4, wide, SBOARD_TITLE_SIZE_Y ); m_TitleLabel.setContentFitted( false ); m_TitleLabel.setParent( this ); // Setup the header (labels like "name", "class", etc..). m_HeaderGrid.SetDimensions( NUM_COLUMNS, 1 ); m_HeaderGrid.SetSpacing( 0, 0 ); for( int i = 0; i < NUM_COLUMNS; i++ ) { if( g_ColumnInfo[i].m_pTitle && g_ColumnInfo[i].m_pTitle[0] == '#' ) m_HeaderLabels[i].setText( CHudTextMessage::BufferedLocaliseTextString( g_ColumnInfo[i].m_pTitle ) ); else if( g_ColumnInfo[i].m_pTitle ) m_HeaderLabels[i].setText( g_ColumnInfo[i].m_pTitle ); int xwide = g_ColumnInfo[i].m_Width; if( ScreenWidth >= 640 ) { xwide = XRES( xwide ); } else if( ScreenWidth == 400 ) { // hack to make 400x300 resolution scoreboard fit if( i == 1 ) { // reduces size of player name cell xwide -= 28; } else if( i == 0 ) { // tracker icon cell xwide -= 8; } } m_HeaderGrid.SetColumnWidth( i, xwide ); m_HeaderGrid.SetEntry( i, 0, &m_HeaderLabels[i] ); m_HeaderLabels[i].setBgColor( 0, 0, 0, 255 ); m_HeaderLabels[i].setFgColor( Scheme::sc_primary1 ); m_HeaderLabels[i].setFont( smallfont ); m_HeaderLabels[i].setContentAlignment( g_ColumnInfo[i].m_Alignment ); int yres = 12; if( ScreenHeight >= 480 ) { yres = YRES( yres ); } m_HeaderLabels[i].setSize( 50, yres ); } // Set the width of the last column to be the remaining space. int ex, ey, ew, eh; m_HeaderGrid.GetEntryBox( NUM_COLUMNS - 2, 0, ex, ey, ew, eh ); m_HeaderGrid.SetColumnWidth( NUM_COLUMNS - 1, ( wide - X_BORDER ) - ( ex + ew ) ); m_HeaderGrid.AutoSetRowHeights(); m_HeaderGrid.setBounds( X_BORDER, SBOARD_TITLE_SIZE_Y, wide - X_BORDER * 2, m_HeaderGrid.GetRowHeight( 0 ) ); m_HeaderGrid.setParent( this ); m_HeaderGrid.setBgColor( 0, 0, 0, 255 ); // Now setup the listbox with the actual player data in it. int headerX, headerY, headerWidth, headerHeight; m_HeaderGrid.getBounds( headerX, headerY, headerWidth, headerHeight ); m_PlayerList.setBounds( headerX, headerY+headerHeight, headerWidth, tall - headerY - headerHeight - 6 ); m_PlayerList.setBgColor( 0, 0, 0, 255 ); m_PlayerList.setParent( this ); for( int row=0; row < NUM_ROWS; row++ ) { CGrid *pGridRow = &m_PlayerGrids[row]; pGridRow->SetDimensions( NUM_COLUMNS, 1 ); for( int col = 0; col < NUM_COLUMNS; col++ ) { m_PlayerEntries[col][row].setContentFitted( false ); m_PlayerEntries[col][row].setRow( row ); m_PlayerEntries[col][row].addInputSignal( this ); pGridRow->SetEntry(col, 0, &m_PlayerEntries[col][row]); } pGridRow->setBgColor( 0, 0, 0, 255 ); pGridRow->SetSpacing( 0, 0 ); pGridRow->CopyColumnWidths( &m_HeaderGrid ); pGridRow->AutoSetRowHeights(); pGridRow->setSize( PanelWidth( pGridRow ), pGridRow->CalcDrawHeight() ); pGridRow->RepositionContents(); m_PlayerList.AddItem( pGridRow ); } // Add the hit test panel. It is invisible and traps mouse clicks so we can go into squelch mode. m_HitTestPanel.setBgColor( 0, 0, 0, 255 ); m_HitTestPanel.setParent( this ); m_HitTestPanel.setBounds( 0, 0, wide, tall ); m_HitTestPanel.addInputSignal( this ); m_pCloseButton = new CommandButton( "x", wide - XRES( 12 + 4 ), YRES( 2 ), XRES( 12 ) , YRES( 12 ) ); m_pCloseButton->setParent( this ); m_pCloseButton->addActionSignal( new CMenuHandler_StringCommandWatch( "-showscores", true ) ); m_pCloseButton->setBgColor( 0, 0, 0, 255 ); m_pCloseButton->setFgColor( 255, 255, 255, 0 ); m_pCloseButton->setFont( tfont ); m_pCloseButton->setBoundKey( (char)255 ); m_pCloseButton->setContentAlignment( Label::a_center ); Initialize(); } //----------------------------------------------------------------------------- // Purpose: Called each time a new level is started. //----------------------------------------------------------------------------- void ScorePanel::Initialize( void ) { // Clear out scoreboard data m_iLastKilledBy = 0; m_fLastKillTime = 0; m_iPlayerNum = 0; m_iNumTeams = 0; memset( g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo ); memset( g_TeamInfo, 0, sizeof g_TeamInfo ); } bool HACK_GetPlayerUniqueID( int iPlayer, char playerID[16] ) { return !!gEngfuncs.GetPlayerUniqueID( iPlayer, playerID ); // TODO remove after testing } //----------------------------------------------------------------------------- // Purpose: Recalculate the internal scoreboard data //----------------------------------------------------------------------------- void ScorePanel::Update() { int i; // Set the title if( gViewPort->m_szServerName[0] != '\0' ) { char sz[MAX_SERVERNAME_LENGTH + 16]; sprintf( sz, "%s", gViewPort->m_szServerName ); m_TitleLabel.setText( sz ); } m_iRows = 0; gViewPort->GetAllPlayersInfo(); // Clear out sorts for (i = 0; i < NUM_ROWS; i++) { m_iSortedRows[i] = 0; m_iIsATeam[i] = TEAM_NO; } for (i = 0; i < MAX_PLAYERS; i++) { m_bHasBeenSorted[i] = false; } // If it's not teamplay, sort all the players. Otherwise, sort the teams. if( !gHUD.m_Teamplay ) SortPlayers( 0, NULL ); else SortTeams(); // set scrollbar range m_PlayerList.SetScrollRange(m_iRows); FillGrid(); if( gViewPort->m_pSpectatorPanel->m_menuVisible ) { m_pCloseButton->setVisible ( true ); } else { m_pCloseButton->setVisible ( false ); } } //----------------------------------------------------------------------------- // Purpose: Sort all the teams //----------------------------------------------------------------------------- void ScorePanel::SortTeams() { // clear out team scores int i; for ( i = 1; i <= m_iNumTeams; i++ ) { if( !g_TeamInfo[i].scores_overriden ) g_TeamInfo[i].frags = g_TeamInfo[i].deaths = 0; g_TeamInfo[i].ping = g_TeamInfo[i].packetloss = 0; } // recalc the team scores, then draw them for( i = 1; i < MAX_PLAYERS; i++ ) { if( g_PlayerInfoList[i].name == 0 ) continue; // empty player slot, skip if( g_PlayerExtraInfo[i].teamname[0] == 0 ) continue; // skip over players who are not in a team // find what team this player is in int j; for ( j = 1; j <= m_iNumTeams; j++ ) { if( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) break; } if( j > m_iNumTeams ) // player is not in a team, skip to the next guy continue; if( !g_TeamInfo[j].scores_overriden ) { g_TeamInfo[j].frags += g_PlayerExtraInfo[i].frags; g_TeamInfo[j].deaths += g_PlayerExtraInfo[i].deaths; } g_TeamInfo[j].ping += g_PlayerInfoList[i].ping; g_TeamInfo[j].packetloss += g_PlayerInfoList[i].packetloss; if( g_PlayerInfoList[i].thisplayer ) g_TeamInfo[j].ownteam = TRUE; else g_TeamInfo[j].ownteam = FALSE; // Set the team's number (used for team colors) g_TeamInfo[j].teamnumber = g_PlayerExtraInfo[i].teamnumber; } // find team ping/packetloss averages for( i = 1; i <= m_iNumTeams; i++ ) { g_TeamInfo[i].already_drawn = FALSE; if( g_TeamInfo[i].players > 0 ) { g_TeamInfo[i].ping /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping g_TeamInfo[i].packetloss /= g_TeamInfo[i].players; // use the average ping of all the players in the team as the teams ping } } // Draw the teams while( 1 ) { int highest_frags = -99999; int lowest_deaths = 99999; int best_team = 0; for( i = 1; i <= m_iNumTeams; i++ ) { if( g_TeamInfo[i].players < 1 ) continue; if( !g_TeamInfo[i].already_drawn && g_TeamInfo[i].frags >= highest_frags ) { if( g_TeamInfo[i].frags > highest_frags || g_TeamInfo[i].deaths < lowest_deaths ) { best_team = i; lowest_deaths = g_TeamInfo[i].deaths; highest_frags = g_TeamInfo[i].frags; } } } // draw the best team on the scoreboard if( !best_team ) break; // Put this team in the sorted list m_iSortedRows[m_iRows] = best_team; m_iIsATeam[m_iRows] = TEAM_YES; g_TeamInfo[best_team].already_drawn = TRUE; // set the already_drawn to be TRUE, so this team won't get sorted again m_iRows++; // Now sort all the players on this team SortPlayers( 0, g_TeamInfo[best_team].name ); } // Add all the players who aren't in a team yet into spectators SortPlayers( TEAM_SPECTATORS, NULL ); } //----------------------------------------------------------------------------- // Purpose: Sort a list of players //----------------------------------------------------------------------------- void ScorePanel::SortPlayers( int iTeam, char *team ) { bool bCreatedTeam = false; // draw the players, in order, and restricted to team if set while ( 1 ) { // Find the top ranking player int highest_frags = -99999; int lowest_deaths = 99999; int best_player; best_player = 0; for ( int i = 1; i < MAX_PLAYERS; i++ ) { if ( m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name && g_PlayerExtraInfo[i].frags >= highest_frags ) { cl_entity_t *ent = gEngfuncs.GetEntityByIndex( i ); if ( ent && !(team && stricmp(g_PlayerExtraInfo[i].teamname, team)) ) { extra_player_info_t *pl_info = &g_PlayerExtraInfo[i]; if ( pl_info->frags > highest_frags || pl_info->deaths < lowest_deaths ) { best_player = i; lowest_deaths = pl_info->deaths; highest_frags = pl_info->frags; } } } } if ( !best_player ) break; // If we haven't created the Team yet, do it first if (!bCreatedTeam && iTeam) { m_iIsATeam[ m_iRows ] = iTeam; m_iRows++; bCreatedTeam = true; } // Put this player in the sorted list m_iSortedRows[ m_iRows ] = best_player; m_bHasBeenSorted[ best_player ] = true; m_iRows++; } if (team) { m_iIsATeam[m_iRows++] = TEAM_BLANK; } } //----------------------------------------------------------------------------- // Purpose: Recalculate the existing teams in the match //----------------------------------------------------------------------------- void ScorePanel::RebuildTeams() { // clear out player counts from teams int i; for ( i = 1; i <= m_iNumTeams; i++ ) { g_TeamInfo[i].players = 0; } // rebuild the team list gViewPort->GetAllPlayersInfo(); m_iNumTeams = 0; for ( i = 1; i < MAX_PLAYERS; i++ ) { if ( g_PlayerInfoList[i].name == NULL ) continue; if ( g_PlayerExtraInfo[i].teamname[0] == 0 ) continue; // skip over players who are not in a team // is this player in an existing team? int j; for ( j = 1; j <= m_iNumTeams; j++ ) { if ( g_TeamInfo[j].name[0] == '\0' ) break; if ( !stricmp( g_PlayerExtraInfo[i].teamname, g_TeamInfo[j].name ) ) break; } if ( j > m_iNumTeams ) { // they aren't in a listed team, so make a new one // search through for an empty team slot for ( j = 1; j <= m_iNumTeams; j++ ) { if ( g_TeamInfo[j].name[0] == '\0' ) break; } m_iNumTeams = Q_max( j, m_iNumTeams ); strncpy( g_TeamInfo[j].name, g_PlayerExtraInfo[i].teamname, MAX_TEAM_NAME ); g_TeamInfo[j].players = 0; } g_TeamInfo[j].players++; } // clear out any empty teams for ( i = 1; i <= m_iNumTeams; i++ ) { if ( g_TeamInfo[i].players < 1 ) memset( &g_TeamInfo[i], 0, sizeof(team_info_t) ); } // Update the scoreboard Update(); } void ScorePanel::FillGrid() { CSchemeManager *pSchemes = gViewPort->GetSchemeManager(); SchemeHandle_t hScheme = pSchemes->getSchemeHandle("Scoreboard Text"); SchemeHandle_t hTitleScheme = pSchemes->getSchemeHandle("Scoreboard Title Text"); SchemeHandle_t hSmallScheme = pSchemes->getSchemeHandle("Scoreboard Small Text"); Font *sfont = pSchemes->getFont(hScheme); Font *tfont = pSchemes->getFont(hTitleScheme); Font *smallfont = pSchemes->getFont(hSmallScheme); // update highlight position int x, y; getApp()->getCursorPos(x, y); cursorMoved(x, y, this); // remove highlight row if we're not in squelch mode if (!GetClientVoiceMgr()->IsInSquelchMode()) { m_iHighlightRow = -1; } bool bNextRowIsGap = false; int row; for(row=0; row < NUM_ROWS; row++) { CGrid *pGridRow = &m_PlayerGrids[row]; pGridRow->SetRowUnderline(0, false, 0, 0, 0, 0, 0); if(row >= m_iRows) { for(int col=0; col < NUM_COLUMNS; col++) m_PlayerEntries[col][row].setVisible(false); continue; } bool bRowIsGap = false; if (bNextRowIsGap) { bNextRowIsGap = false; bRowIsGap = true; } for(int col=0; col < NUM_COLUMNS; col++) { CLabelHeader *pLabel = &m_PlayerEntries[col][row]; pLabel->setVisible(true); pLabel->setText2(""); pLabel->setImage(NULL); pLabel->setFont(sfont); pLabel->setTextOffset(0, 0); int rowheight = 13; if (ScreenHeight > 480) { rowheight = YRES(rowheight); } else { // more tweaking, make sure icons fit at low res rowheight = 15; } pLabel->setSize(pLabel->getWide(), rowheight); pLabel->setBgColor(0, 0, 0, 255); char sz[128]; hud_player_info_t *pl_info = NULL; team_info_t *team_info = NULL; if (m_iIsATeam[row] == TEAM_BLANK) { pLabel->setText(" "); continue; } else if ( m_iIsATeam[row] == TEAM_YES ) { // Get the team's data team_info = &g_TeamInfo[ m_iSortedRows[row] ]; // team color text for team names pLabel->setFgColor( iTeamColors[team_info->teamnumber % iNumberOfTeamColors][0], iTeamColors[team_info->teamnumber % iNumberOfTeamColors][1], iTeamColors[team_info->teamnumber % iNumberOfTeamColors][2], 0 ); // different height for team header rows rowheight = 20; if (ScreenHeight >= 480) { rowheight = YRES(rowheight); } pLabel->setSize(pLabel->getWide(), rowheight); pLabel->setFont(tfont); pGridRow->SetRowUnderline( 0, true, YRES(3), iTeamColors[team_info->teamnumber % iNumberOfTeamColors][0], iTeamColors[team_info->teamnumber % iNumberOfTeamColors][1], iTeamColors[team_info->teamnumber % iNumberOfTeamColors][2], 0 ); } else if ( m_iIsATeam[row] == TEAM_SPECTATORS ) { // grey text for spectators pLabel->setFgColor(100, 100, 100, 0); // different height for team header rows rowheight = 20; if (ScreenHeight >= 480) { rowheight = YRES(rowheight); } pLabel->setSize(pLabel->getWide(), rowheight); pLabel->setFont(tfont); pGridRow->SetRowUnderline(0, true, YRES(3), 100, 100, 100, 0); } else { // team color text for player names pLabel->setFgColor( iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][0], iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][1], iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][2], 0 ); // Get the player's data pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ]; // Set background color if ( pl_info->thisplayer ) // if it is their name, draw it a different color { // Highlight this player pLabel->setFgColor(Scheme::sc_white); pLabel->setBgColor( iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][0], iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][1], iTeamColors[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber % iNumberOfTeamColors ][2], 196 ); } else if ( m_iSortedRows[row] == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime ) { // Killer's name pLabel->setBgColor( 255,0,0, 255 - ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) ); } } // Align if (col == COLUMN_NAME || col == COLUMN_CLASS) { pLabel->setContentAlignment( vgui::Label::a_west ); } else if (col == COLUMN_TRACKER) { pLabel->setContentAlignment( vgui::Label::a_center ); } else { pLabel->setContentAlignment( vgui::Label::a_east ); } // Fill out with the correct data strcpy(sz, ""); if ( m_iIsATeam[row] ) { char sz2[128]; switch (col) { case COLUMN_NAME: if ( m_iIsATeam[row] == TEAM_SPECTATORS ) { sprintf( sz2, "%s", CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) ); } else { sprintf( sz2, "%s", gViewPort->GetTeamName(team_info->teamnumber) ); } strcpy(sz, sz2); // Append the number of players if ( m_iIsATeam[row] == TEAM_YES ) { if (team_info->players == 1) { sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player" ) ); } else { sprintf(sz2, "(%d %s)", team_info->players, CHudTextMessage::BufferedLocaliseTextString( "#Player_plural" ) ); } pLabel->setText2(sz2); pLabel->setFont2(smallfont); } break; case COLUMN_VOICE: break; case COLUMN_CLASS: break; case COLUMN_KILLS: if ( m_iIsATeam[row] == TEAM_YES ) sprintf(sz, "%d", team_info->frags ); break; case COLUMN_DEATHS: if ( m_iIsATeam[row] == TEAM_YES ) sprintf(sz, "%d", team_info->deaths ); break; case COLUMN_LATENCY: if ( m_iIsATeam[row] == TEAM_YES ) sprintf(sz, "%d", team_info->ping ); break; default: break; } } else { bool bShowClass = false; switch (col) { case COLUMN_NAME: /* if (g_pTrackerUser) { int playerSlot = m_iSortedRows[row]; int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); const char *trackerName = g_pTrackerUser->GetUserName(trackerID); if (trackerName && *trackerName) { sprintf(sz, " (%s)", trackerName); pLabel->setText2(sz); } } */ sprintf(sz, "%s ", pl_info->name); break; case COLUMN_VOICE: sz[0] = 0; // in HLTV mode allow spectator to turn on/off commentator voice //if (!pl_info->thisplayer || gEngfuncs.IsSpectateOnly() ) //{ GetClientVoiceMgr()->UpdateSpeakerImage(pLabel, m_iSortedRows[row]); //} break; case COLUMN_CLASS: // No class for other team's members (unless allied or spectator) if ( gViewPort && EV_TFC_IsAllyTeam( g_iTeamNumber, g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ) ) bShowClass = true; // Don't show classes if this client hasnt picked a team yet if ( g_iTeamNumber == 0 ) bShowClass = false; if (bShowClass) { // Only print Civilian if this team are all civilians bool bNoClass = false; if ( g_PlayerExtraInfo[ m_iSortedRows[row] ].playerclass == 0 ) { if ( gViewPort->GetValidClasses( g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ) != -1 ) bNoClass = true; } if (bNoClass) sz[0] = '\0'; else sprintf( sz, "%s", CHudTextMessage::BufferedLocaliseTextString( sLocalisedClasses[ g_PlayerExtraInfo[ m_iSortedRows[row] ].playerclass ] ) ); } else { sz[0] = '\0'; } break; case COLUMN_TRACKER: /* if (g_pTrackerUser) { int playerSlot = m_iSortedRows[row]; int trackerID = gEngfuncs.GetTrackerIDForPlayer(playerSlot); if (g_pTrackerUser->IsFriend(trackerID) && trackerID != g_pTrackerUser->GetTrackerID()) { pLabel->setImage(m_pTrackerIcon); pLabel->setFgColorAsImageColor(false); m_pTrackerIcon->setColor(Color(255, 255, 255, 0)); } } */ break; case COLUMN_KILLS: sprintf(sz, "%d", g_PlayerExtraInfo[ m_iSortedRows[row] ].frags ); break; case COLUMN_DEATHS: sprintf(sz, "%d", g_PlayerExtraInfo[ m_iSortedRows[row] ].deaths ); break; case COLUMN_LATENCY: sprintf(sz, "%d", g_PlayerInfoList[ m_iSortedRows[row] ].ping ); break; default: break; } } pLabel->setText(sz); } } for(row=0; row < NUM_ROWS; row++) { CGrid *pGridRow = &m_PlayerGrids[row]; pGridRow->AutoSetRowHeights(); pGridRow->setSize(PanelWidth(pGridRow), pGridRow->CalcDrawHeight()); pGridRow->RepositionContents(); } // hack, for the thing to resize m_PlayerList.getSize(x, y); m_PlayerList.setSize(x, y); } //----------------------------------------------------------------------------- // Purpose: Setup highlights for player names in scoreboard //----------------------------------------------------------------------------- void ScorePanel::DeathMsg( int killer, int victim ) { // if we were the one killed, or the world killed us, set the scoreboard to indicate suicide if ( victim == m_iPlayerNum || killer == 0 ) { m_iLastKilledBy = killer ? killer : m_iPlayerNum; m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds if ( killer == m_iPlayerNum ) m_iLastKilledBy = m_iPlayerNum; } } void ScorePanel::Open( void ) { RebuildTeams(); setVisible(true); m_HitTestPanel.setVisible(true); } void ScorePanel::mousePressed(MouseCode code, Panel* panel) { if(gHUD.m_iIntermission) return; if (!GetClientVoiceMgr()->IsInSquelchMode()) { GetClientVoiceMgr()->StartSquelchMode(); m_HitTestPanel.setVisible(false); } else if (m_iHighlightRow >= 0) { // mouse has been pressed, toggle mute state int iPlayer = m_iSortedRows[m_iHighlightRow]; if (iPlayer > 0) { // print text message hud_player_info_t *pl_info = &g_PlayerInfoList[iPlayer]; if (pl_info && pl_info->name && pl_info->name[0]) { char string[256]; if (GetClientVoiceMgr()->IsPlayerBlocked(iPlayer)) { char string1[1024]; // remove mute GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, false); sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Unmuted" ), pl_info->name ); sprintf( string, "%c** %s\n", HUD_PRINTTALK, string1 ); gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, strlen(string)+1, string ); } else { char string1[1024]; char string2[1024]; // mute the player GetClientVoiceMgr()->SetPlayerBlockedState(iPlayer, true); sprintf( string1, CHudTextMessage::BufferedLocaliseTextString( "#Muted" ), pl_info->name ); sprintf( string2, "%s", CHudTextMessage::BufferedLocaliseTextString( "#No_longer_hear_that_player" ) ); sprintf( string, "%c** %s %s\n", HUD_PRINTTALK, string1, string2 ); gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, strlen(string)+1, string ); } } } } } void ScorePanel::cursorMoved(int x, int y, Panel *panel) { if (GetClientVoiceMgr()->IsInSquelchMode()) { // look for which cell the mouse is currently over for (int i = 0; i < NUM_ROWS; i++) { int row, col; if (m_PlayerGrids[i].getCellAtPoint(x, y, row, col)) { MouseOverCell(i, col); return; } } } } //----------------------------------------------------------------------------- // Purpose: Handles mouse movement over a cell // Input : row - // col - //----------------------------------------------------------------------------- void ScorePanel::MouseOverCell(int row, int col) { CLabelHeader *label = &m_PlayerEntries[col][row]; // clear the previously highlighted label if (m_pCurrentHighlightLabel != label) { m_pCurrentHighlightLabel = NULL; m_iHighlightRow = -1; } if (!label) return; // don't act on teams if (m_iIsATeam[row] != TEAM_NO) return; // don't act on disconnected players or ourselves hud_player_info_t *pl_info = &g_PlayerInfoList[ m_iSortedRows[row] ]; if (!pl_info->name || !pl_info->name[0]) return; if (pl_info->thisplayer && !gEngfuncs.IsSpectateOnly() ) return; // setup the new highlight m_pCurrentHighlightLabel = label; m_iHighlightRow = row; } //----------------------------------------------------------------------------- // Purpose: Label paint functions - take into account current highligh status //----------------------------------------------------------------------------- void CLabelHeader::paintBackground() { Color oldBg; getBgColor(oldBg); if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) { setBgColor(134, 91, 19, 0); } Panel::paintBackground(); setBgColor(oldBg); } //----------------------------------------------------------------------------- // Purpose: Label paint functions - take into account current highligh status //----------------------------------------------------------------------------- void CLabelHeader::paint() { Color oldFg; getFgColor(oldFg); if (gViewPort->GetScoreBoard()->m_iHighlightRow == _row) { setFgColor(255, 255, 255, 0); } // draw text int x, y, iwide, itall; getTextSize(iwide, itall); calcAlignment(iwide, itall, x, y); _dualImage->setPos(x, y); int x1, y1; _dualImage->GetImage(1)->getPos(x1, y1); _dualImage->GetImage(1)->setPos(_gap, y1); _dualImage->doPaint(this); // get size of the panel and the image if (_image) { Color imgColor; getFgColor( imgColor ); if( _useFgColorAsImageColor ) { _image->setColor( imgColor ); } _image->getSize(iwide, itall); calcAlignment(iwide, itall, x, y); _image->setPos(x, y); _image->doPaint(this); } setFgColor(oldFg[0], oldFg[1], oldFg[2], oldFg[3]); } void CLabelHeader::calcAlignment(int iwide, int itall, int &x, int &y) { // calculate alignment ourselves, since vgui is so broken int wide, tall; getSize(wide, tall); x = 0, y = 0; // align left/right switch (_contentAlignment) { // left case Label::a_northwest: case Label::a_west: case Label::a_southwest: { x = 0; break; } // center case Label::a_north: case Label::a_center: case Label::a_south: { x = (wide - iwide) / 2; break; } // right case Label::a_northeast: case Label::a_east: case Label::a_southeast: { x = wide - iwide; break; } } // top/down switch (_contentAlignment) { // top case Label::a_northwest: case Label::a_north: case Label::a_northeast: { y = 0; break; } // center case Label::a_west: case Label::a_center: case Label::a_east: { y = (tall - itall) / 2; break; } // south case Label::a_southwest: case Label::a_south: case Label::a_southeast: { y = tall - itall; break; } } // don't clip to Y // if (y < 0) // { // y = 0; // } if (x < 0) { x = 0; } x += _offset[0]; y += _offset[1]; }