mirror of
https://github.com/ENSL/NS.git
synced 2024-11-15 09:21:54 +00:00
60007652a3
git-svn-id: https://unknownworlds.svn.cloudforge.com/ns1@21 67975925-1194-0748-b3d5-c16f83f1a3a1
1406 lines
41 KiB
C++
1406 lines
41 KiB
C++
//=========== (C) Copyright 1999 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: 2002/10/24 21:13:15 $
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// $Log: vgui_ScorePanel.cpp,v $
|
|
// Revision 1.15 2002/10/24 21:13:15 Flayra
|
|
// - Fixed gamma correction for auth icons
|
|
//
|
|
// Revision 1.14 2002/10/18 22:13:48 Flayra
|
|
// - Gamma-correction for auth icons
|
|
//
|
|
// Revision 1.13 2002/10/16 00:37:33 Flayra
|
|
// - Added support for authentication in scoreboard
|
|
//
|
|
// Revision 1.12 2002/09/09 19:42:55 Flayra
|
|
// - Fixed problem where scores were sometimes displayed for marines
|
|
//
|
|
// Revision 1.11 2002/08/31 18:02:11 Flayra
|
|
// - Work at VALVe
|
|
//
|
|
// Revision 1.10 2002/08/09 00:11:33 Flayra
|
|
// - Fixed scoreboard
|
|
//
|
|
// Revision 1.9 2002/07/08 16:15:13 Flayra
|
|
// - Refactored team color management
|
|
//
|
|
// Revision 1.8 2002/06/25 17:06:48 Flayra
|
|
// - Removed memory overwrite from hlcoder list
|
|
//
|
|
// Revision 1.7 2002/06/03 16:12:21 Flayra
|
|
// - Show player status for players on your team (LEV1, GEST, COMM, etc.)
|
|
//
|
|
// Revision 1.6 2002/04/16 19:30:21 Charlie
|
|
// - Fixed crash when holding tab right when joining server, added "REIN" status
|
|
//
|
|
// Revision 1.5 2002/03/27 21:17:18 Charlie
|
|
// - Scoreboard now shows alien scores, and doesn't show marine scores. Also draws DEAD and COMM indicators, and scoring is handled properly for aliens (points for kills, buildings)
|
|
//
|
|
// Revision 1.4 2002/03/08 02:37:52 Charlie
|
|
// - Refactored crappy-ass score panel. It's not perfect, but it's better. Removed TFC code, added DEAD and COMM tags to scoreboard.
|
|
//
|
|
// Revision 1.3 2001/11/13 17:51:02 Charlie
|
|
// - Increased max teams, changed team colors (allow aliens vs. aliens and fronts vs. fronts), general scoreboard support
|
|
//
|
|
// Revision 1.2 2001/09/13 22:28:01 Charlie
|
|
// - Updated NS with new Half-life 1108 patch in preparation for voice support and spectator mode
|
|
//
|
|
// Revision 1.1.1.1.2.1 2001/09/13 14:42:30 Charlie
|
|
// - HL1108
|
|
//
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================
|
|
|
|
|
|
#include<VGUI_LineBorder.h>
|
|
|
|
#include "hud.h"
|
|
#include "cl_util.h"
|
|
#include "common/const.h"
|
|
#include "common/entity_state.h"
|
|
#include "common/cl_entity.h"
|
|
#include "vgui_TeamFortressViewport.h"
|
|
#include "vgui_ScorePanel.h"
|
|
#include "..\game_shared\vgui_helpers.h"
|
|
#include "..\game_shared\vgui_loadtga.h"
|
|
#include "mod/AvHConstants.h"
|
|
#include "mod/AvHTitles.h"
|
|
#include "vgui_SpectatorPanel.h"
|
|
#include "cl_dll/demo.h"
|
|
#include "mod/AvHServerVariables.h"
|
|
#include "util\STLUtil.h"
|
|
#include "ui/ScoreboardIcon.h"
|
|
|
|
#include "common/ITrackerUser.h"
|
|
extern ITrackerUser *g_pTrackerUser;
|
|
|
|
hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine
|
|
extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll
|
|
team_info_t g_TeamInfo[MAX_TEAMS+1];
|
|
int g_IsSpectator[MAX_PLAYERS+1];
|
|
|
|
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)
|
|
|
|
void LoadData(void* inBuffer, const unsigned char* inData, int inSizeToCopy, int& inSizeVariable);
|
|
void SaveData(unsigned char* inBuffer, const void* inData, int inSizeToCopy, int& inSizeVariable);
|
|
|
|
int ScorePanel_InitializeDemoPlayback(int inSize, unsigned char* inBuffer)
|
|
{
|
|
int theBytesRead = 0;
|
|
|
|
LoadData(&g_PlayerInfoList, inBuffer, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theBytesRead);
|
|
LoadData(&g_PlayerExtraInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theBytesRead);
|
|
LoadData(&g_TeamInfo, inBuffer, (MAX_PLAYERS+1)*sizeof(team_info_t), theBytesRead);
|
|
LoadData(&g_IsSpectator, inBuffer, (MAX_PLAYERS+1)*sizeof(int), theBytesRead);
|
|
|
|
return theBytesRead;
|
|
}
|
|
|
|
void ScorePanel_InitializeDemoRecording()
|
|
{
|
|
// Now save out team info
|
|
int theTotalSize = (MAX_PLAYERS + 1)*(sizeof(hud_player_info_t) + sizeof(extra_player_info_t) + sizeof(team_info_t) + sizeof(int));
|
|
unsigned char* theCharArray = new unsigned char[theTotalSize];
|
|
if(theCharArray)
|
|
{
|
|
int theCounter = 0;
|
|
SaveData(theCharArray, &g_PlayerInfoList, (MAX_PLAYERS+1)*sizeof(hud_player_info_t), theCounter);
|
|
SaveData(theCharArray, &g_PlayerExtraInfo, (MAX_PLAYERS+1)*sizeof(extra_player_info_t), theCounter);
|
|
SaveData(theCharArray, &g_TeamInfo, (MAX_PLAYERS+1)*sizeof(team_info_t), theCounter);
|
|
SaveData(theCharArray, &g_IsSpectator, (MAX_PLAYERS+1)*sizeof(int), theCounter);
|
|
|
|
Demo_WriteBuffer(TYPE_PLAYERINFO, theTotalSize, theCharArray);
|
|
}
|
|
}
|
|
|
|
// Column sizes
|
|
class SBColumnInfo
|
|
{
|
|
public:
|
|
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, 24, Label::a_east}, // status icons
|
|
{NULL, 150, Label::a_east}, // name
|
|
{NULL, 56, Label::a_east}, // class
|
|
{"#SCORE", 40, Label::a_east}, // score
|
|
{"#KILLS", 40, Label::a_east}, // kills
|
|
{"#DEATHS", 40, Label::a_east}, // deaths
|
|
{"#LATENCY", 40, Label::a_east}, // ping
|
|
{"#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);
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
vgui::Color BuildColor( int R, int G, int B, float gamma )
|
|
{
|
|
ASSERT( gamma != 0 );
|
|
return vgui::Color( R/gamma, G/gamma, B/gamma, 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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");
|
|
SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text");
|
|
Font *tfont = pSchemes->getFont(hTitleScheme);
|
|
Font *smallfont = pSchemes->getFont(hSmallScheme);
|
|
Font *tinyfont = pSchemes->getFont(hTinyScheme);
|
|
|
|
setBgColor(0, 0, 0, 96);
|
|
m_pCurrentHighlightLabel = NULL;
|
|
m_iHighlightRow = -1;
|
|
m_iIconFrame = 0;
|
|
m_iLastFrameIncrementTime = gHUD.GetTimeOfLastUpdate();
|
|
|
|
// 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].setBgColor(0,0,0,255);
|
|
|
|
int theColorIndex = 0;
|
|
Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope());
|
|
int theR, theG, theB, theA;
|
|
gammaAdjustedTeamColor.getColor(theR, theG, theB, theA);
|
|
m_HeaderLabels[i].setFgColor(theR, theG, theB, theA);
|
|
|
|
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(2, 0);f
|
|
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;
|
|
for( int counter = 0; counter < MAX_PLAYERS+1; counter++ )
|
|
{
|
|
delete g_PlayerExtraInfo[counter].icon;
|
|
}
|
|
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 );
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Recalculate the internal scoreboard data
|
|
//-----------------------------------------------------------------------------
|
|
void ScorePanel::Update()
|
|
{
|
|
// Set the title
|
|
string theTitleName;
|
|
|
|
if (gViewPort->m_szServerName)
|
|
{
|
|
int iServerNameLength = max((int)strlen(gViewPort->m_szServerName),MAX_SERVERNAME_LENGTH);
|
|
theTitleName += string(gViewPort->m_szServerName,iServerNameLength);
|
|
}
|
|
|
|
string theMapName = gHUD.GetMapName();
|
|
if(theMapName != "")
|
|
{
|
|
if(theTitleName != "")
|
|
{
|
|
theTitleName += " ";
|
|
}
|
|
|
|
theTitleName += "(";
|
|
theTitleName += theMapName;
|
|
theTitleName += ")";
|
|
}
|
|
|
|
m_TitleLabel.setText(theTitleName.c_str());
|
|
|
|
int theColorIndex = 0;
|
|
|
|
// Set gamma-correct title color
|
|
Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope());
|
|
|
|
int theR, theG, theB, theA;
|
|
gammaAdjustedTeamColor.getColor(theR, theG, theB, theA);
|
|
|
|
m_TitleLabel.setFgColor(theR, theG, theB, theA);
|
|
|
|
m_iRows = 0;
|
|
gViewPort->GetAllPlayersInfo();
|
|
|
|
// Clear out sorts
|
|
int i = 0;
|
|
for (i = 0; i < NUM_ROWS; i++)
|
|
{
|
|
m_iSortedRows[i] = 0;
|
|
m_iIsATeam[i] = TEAM_IND;
|
|
}
|
|
|
|
// Fix for memory overrun bug
|
|
for (i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
m_bHasBeenSorted[i] = false;
|
|
}
|
|
|
|
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
|
|
float theCurrentTime = gHUD.GetTimeOfLastUpdate();
|
|
|
|
for ( int i = 1; i <= m_iNumTeams; i++ )
|
|
{
|
|
if ( !g_TeamInfo[i].scores_overriden )
|
|
g_TeamInfo[i].score = 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 == NULL )
|
|
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
|
|
for ( int 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].score += g_PlayerExtraInfo[i].score;
|
|
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
|
|
}
|
|
}
|
|
|
|
SortActivePlayers(kMarine1Team);
|
|
SortActivePlayers(kAlien1Team);
|
|
SortActivePlayers(kMarine2Team);
|
|
SortActivePlayers(kAlien2Team);
|
|
SortActivePlayers(kSpectatorTeam);
|
|
SortActivePlayers(kUndefinedTeam);
|
|
}
|
|
|
|
void ScorePanel::SortActivePlayers(char* inTeam, bool inSortByEntityIndex)
|
|
{
|
|
for(int i = 1; i <= m_iNumTeams; i++)
|
|
{
|
|
if(!strcmp(g_TeamInfo[i].name, inTeam))
|
|
{
|
|
int best_team = i;
|
|
|
|
// 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, inSortByEntityIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sort a list of players
|
|
//-----------------------------------------------------------------------------
|
|
void ScorePanel::SortPlayers( int iTeam, char *team, bool inSortByEntityIndex)
|
|
{
|
|
bool bCreatedTeam = false;
|
|
|
|
// draw the players, in order, and restricted to team if set
|
|
while ( 1 )
|
|
{
|
|
// Find the top ranking player
|
|
int theBestTotalScore = -99999;
|
|
int theBestDeaths = 0;
|
|
int theBestPlayer = 0;
|
|
|
|
for ( int i = 1; i <= MAX_PLAYERS; i++ )
|
|
{
|
|
if ( m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name )
|
|
{
|
|
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];
|
|
|
|
// Sort by player index to mask marine status
|
|
if(inSortByEntityIndex)
|
|
{
|
|
if((theBestPlayer == 0) || (i < theBestPlayer))
|
|
{
|
|
theBestPlayer = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// overall rank = score + kills (with least deaths breaking ties)
|
|
int thePlayerScore = pl_info->score;
|
|
int thePlayerDeaths = pl_info->deaths;
|
|
if((thePlayerScore > theBestTotalScore) || ((thePlayerScore == theBestTotalScore) && (pl_info->deaths < theBestDeaths)))
|
|
{
|
|
theBestPlayer = i;
|
|
theBestTotalScore = thePlayerScore;
|
|
theBestDeaths = thePlayerDeaths;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !theBestPlayer )
|
|
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 ] = theBestPlayer;
|
|
m_bHasBeenSorted[ theBestPlayer ] = 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
|
|
for ( int 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?
|
|
for ( int 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 ( int j = 1; j <= m_iNumTeams; j++ )
|
|
{
|
|
if ( g_TeamInfo[j].name[0] == '\0' )
|
|
break;
|
|
}
|
|
m_iNumTeams = 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();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int ScorePanel::GetIconFrame(void)
|
|
{
|
|
const static int kIconFrameDuration = 0.25;
|
|
|
|
int current_time = gHUD.GetTimeOfLastUpdate();
|
|
if( (m_iLastFrameIncrementTime - current_time) > kIconFrameDuration )
|
|
{
|
|
m_iLastFrameIncrementTime = current_time;
|
|
m_iIconFrame++;
|
|
if( m_iIconFrame >= 1000 )
|
|
{
|
|
m_iIconFrame = 0;
|
|
}
|
|
}
|
|
return m_iIconFrame;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
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");
|
|
SchemeHandle_t hTinyScheme = pSchemes->getSchemeHandle("Scoreboard Tiny Text");
|
|
|
|
Font *sfont = pSchemes->getFont(hScheme);
|
|
Font *tfont = pSchemes->getFont(hTitleScheme);
|
|
Font *smallfont = pSchemes->getFont(hSmallScheme);
|
|
Font *tinyfont = pSchemes->getFont(hTinyScheme);
|
|
|
|
// 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;
|
|
|
|
for(int 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;
|
|
}
|
|
|
|
// Get the player's data
|
|
int theSortedRow = m_iSortedRows[row];
|
|
hud_player_info_t* pl_info = &g_PlayerInfoList[theSortedRow];
|
|
extra_player_info_t* theExtraPlayerInfo = &g_PlayerExtraInfo[theSortedRow];
|
|
int thePlayerClass = theExtraPlayerInfo->playerclass;
|
|
short theTeamNumber = theExtraPlayerInfo->teamnumber;
|
|
bool thePlayerIsDead = false;
|
|
switch( thePlayerClass )
|
|
{
|
|
case PLAYERCLASS_DEAD_MARINE:
|
|
case PLAYERCLASS_DEAD_ALIEN:
|
|
case PLAYERCLASS_REINFORCING:
|
|
thePlayerIsDead = true;
|
|
break;
|
|
}
|
|
|
|
// Code to test DEBUG
|
|
#if 0
|
|
#ifdef DEBUG
|
|
extern int gGlobalDebugAuth;
|
|
thePlayerAuthentication = 1;
|
|
thePlayerAuthentication <<= gGlobalDebugAuth;
|
|
#endif
|
|
#endif
|
|
|
|
team_info_t* team_info = &g_TeamInfo[m_iSortedRows[row]];
|
|
int theColorIndex = theTeamNumber % iNumberOfTeamColors;
|
|
|
|
int theLocalPlayerTeam = 0;
|
|
if(gEngfuncs.GetLocalPlayer())
|
|
{
|
|
theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team;
|
|
}
|
|
|
|
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];
|
|
|
|
Color gammaAdjustedTeamColor = BuildColor(kTeamColors[theColorIndex][0], kTeamColors[theColorIndex][1], kTeamColors[theColorIndex][2], gHUD.GetGammaSlope());
|
|
pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0);
|
|
|
|
if (m_iIsATeam[row] == TEAM_BLANK)
|
|
{
|
|
pLabel->setText(" ");
|
|
continue;
|
|
}
|
|
else if ( m_iIsATeam[row] == TEAM_YES )
|
|
{
|
|
theColorIndex = team_info->teamnumber % iNumberOfTeamColors;
|
|
|
|
// team color text for team names
|
|
|
|
|
|
|
|
// 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),
|
|
gammaAdjustedTeamColor[0],
|
|
gammaAdjustedTeamColor[1],
|
|
gammaAdjustedTeamColor[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
|
|
{
|
|
if(thePlayerIsDead)
|
|
{
|
|
pLabel->setFgColor(255, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// team color text for player names
|
|
pLabel->setFgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 0);
|
|
}
|
|
|
|
// Set background color
|
|
if ( pl_info && pl_info->thisplayer ) // if it is their name, draw it a different color
|
|
{
|
|
// Highlight this player
|
|
pLabel->setFgColor(Scheme::sc_white);
|
|
pLabel->setBgColor(gammaAdjustedTeamColor[0], gammaAdjustedTeamColor[1], gammaAdjustedTeamColor[2], 196 );
|
|
}
|
|
else if ( theSortedRow == 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
|
|
switch(col)
|
|
{
|
|
case COLUMN_NAME:
|
|
case COLUMN_CLASS:
|
|
pLabel->setContentAlignment( vgui::Label::a_west );
|
|
break;
|
|
|
|
case COLUMN_TRACKER:
|
|
case COLUMN_RANK_ICON:
|
|
case COLUMN_VOICE:
|
|
pLabel->setContentAlignment( vgui::Label::a_center );
|
|
break;
|
|
|
|
case COLUMN_SCORE:
|
|
case COLUMN_KILLS:
|
|
case COLUMN_DEATHS:
|
|
case COLUMN_LATENCY:
|
|
default:
|
|
pLabel->setContentAlignment( vgui::Label::a_east );
|
|
break;
|
|
}
|
|
|
|
// 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, CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) );
|
|
}
|
|
else
|
|
{
|
|
if(team_info)
|
|
{
|
|
sprintf( sz2, gViewPort->GetTeamName(team_info->teamnumber) );
|
|
}
|
|
}
|
|
|
|
strcpy(sz, sz2);
|
|
|
|
// Append the number of players
|
|
if ( m_iIsATeam[row] == TEAM_YES && team_info)
|
|
{
|
|
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_SCORE:
|
|
// Don't show score for enemies unless spectating or in RR
|
|
if ((m_iIsATeam[row] == TEAM_YES) && team_info && ((theLocalPlayerTeam == 0) || (theLocalPlayerTeam == team_info->teamnumber)))
|
|
sprintf(sz, "%d", team_info->score);
|
|
break;
|
|
case COLUMN_KILLS:
|
|
if ((m_iIsATeam[row] == TEAM_YES) && team_info)
|
|
sprintf(sz, "%d", team_info->frags );
|
|
break;
|
|
case COLUMN_DEATHS:
|
|
if ((m_iIsATeam[row] == TEAM_YES) && team_info)
|
|
sprintf(sz, "%d", team_info->deaths );
|
|
break;
|
|
case COLUMN_LATENCY:
|
|
if ((m_iIsATeam[row] == TEAM_YES) && team_info)
|
|
sprintf(sz, "%d", team_info->ping );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Are these stats for an enemy? Score and other stats shouldn't be drawn for enemies.
|
|
bool theIsForEnemy = false;
|
|
|
|
int theLocalPlayerTeam = 0;
|
|
if(gEngfuncs.GetLocalPlayer())
|
|
{
|
|
theLocalPlayerTeam = gEngfuncs.GetLocalPlayer()->curstate.team;
|
|
}
|
|
|
|
if((theLocalPlayerTeam != 0) && (theExtraPlayerInfo->teamnumber != theLocalPlayerTeam))
|
|
{
|
|
theIsForEnemy = true;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if(pl_info)
|
|
{
|
|
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 && (!pl_info->thisplayer || gEngfuncs.IsSpectateOnly()))
|
|
{
|
|
GetClientVoiceMgr()->UpdateSpeakerImage(pLabel, theSortedRow);
|
|
}
|
|
break;
|
|
case COLUMN_CLASS:
|
|
// No class for other team's members (unless allied or spectator, and make sure player is on our team)
|
|
strcpy(sz, "");
|
|
|
|
if(team_info && ((theLocalPlayerTeam == theTeamNumber) || (gHUD.GetPlayMode() == PLAYMODE_OBSERVER)))
|
|
{
|
|
switch(thePlayerClass)
|
|
{
|
|
case (int)(PLAYERCLASS_DEAD_MARINE):
|
|
case (int)(PLAYERCLASS_DEAD_ALIEN):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDead));
|
|
break;
|
|
case (int)(PLAYERCLASS_REINFORCING):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcing));
|
|
break;
|
|
case (int)(PLAYERCLASS_REINFORCINGCOMPLETE):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassReinforcingComplete));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_JETPACK_MARINE):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassJetpackMarine));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_HEAVY_MARINE):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassHeavyMarine));
|
|
break;
|
|
case (int)(PLAYERCLASS_COMMANDER):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassCommander));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_LEVEL1):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel1));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_LEVEL2):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel2));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_LEVEL3):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel3));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_LEVEL4):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel4));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_LEVEL5):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassLevel5));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_DIGESTING):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassDigesting));
|
|
break;
|
|
case (int)(PLAYERCLASS_ALIVE_GESTATING):
|
|
sprintf(sz, "%s", CHudTextMessage::BufferedLocaliseTextString(kClassGestating));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COLUMN_RANK_ICON:
|
|
if( theExtraPlayerInfo->icon )
|
|
{
|
|
vgui::Bitmap* image = theExtraPlayerInfo->icon->getImage( this->GetIconFrame() );
|
|
if( image ) { pLabel->setImage( image ); }
|
|
}
|
|
break;
|
|
case COLUMN_SCORE:
|
|
if(!theIsForEnemy)
|
|
{
|
|
const float kDeltaDisplayTime = 3.0f;
|
|
float theTimeSinceChange = gHUD.GetTimeOfLastUpdate() - theExtraPlayerInfo->timeOfLastScoreChange;
|
|
if((theExtraPlayerInfo->score > theExtraPlayerInfo->lastScore) && (theTimeSinceChange > 0) && (theTimeSinceChange < kDeltaDisplayTime) && (theExtraPlayerInfo->teamnumber != 0))
|
|
{
|
|
// draw score with change
|
|
int theDelta = (theExtraPlayerInfo->score - theExtraPlayerInfo->lastScore);
|
|
sprintf(sz, "(+%d) %d", theDelta, theExtraPlayerInfo->score);
|
|
}
|
|
else
|
|
{
|
|
sprintf(sz, "%d", theExtraPlayerInfo->score);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case COLUMN_KILLS:
|
|
sprintf(sz, "%d", theExtraPlayerInfo->frags);
|
|
break;
|
|
|
|
case COLUMN_DEATHS:
|
|
sprintf(sz, "%d", theExtraPlayerInfo->deaths);
|
|
break;
|
|
case COLUMN_LATENCY:
|
|
if(pl_info)
|
|
{
|
|
sprintf(sz, "%d", pl_info->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);
|
|
}
|
|
|
|
bool ScorePanel::SetSquelchMode(bool inMode)
|
|
{
|
|
bool theSuccess = false;
|
|
|
|
if(inMode && !GetClientVoiceMgr()->IsInSquelchMode())
|
|
{
|
|
GetClientVoiceMgr()->StartSquelchMode();
|
|
m_HitTestPanel.setVisible(false);
|
|
theSuccess = true;
|
|
}
|
|
else if(!inMode && GetClientVoiceMgr()->IsInSquelchMode())
|
|
{
|
|
GetClientVoiceMgr()->StopSquelchMode();
|
|
theSuccess = true;
|
|
}
|
|
|
|
return theSuccess;
|
|
}
|
|
|
|
void ScorePanel::mousePressed(MouseCode code, Panel* panel)
|
|
{
|
|
if(gHUD.m_iIntermission)
|
|
return;
|
|
|
|
if (!GetClientVoiceMgr()->IsInSquelchMode())
|
|
{
|
|
//GetClientVoiceMgr()->StartSquelchMode();
|
|
//m_HitTestPanel.setVisible(false);
|
|
this->SetSquelchMode(true);
|
|
}
|
|
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, (int)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, CHudTextMessage::BufferedLocaliseTextString( "#No_longer_hear_that_player" ) );
|
|
sprintf( string, "%c** %s %s\n", HUD_PRINTTALK, string1, string2 );
|
|
|
|
gHUD.m_TextMessage.MsgFunc_TextMsg(NULL, (int)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_IND)
|
|
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;
|
|
|
|
// only act on audible players
|
|
if (!GetClientVoiceMgr()->IsPlayerAudible(m_iSortedRows[row]))
|
|
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];
|
|
}
|