forked from valve/halflife-sdk
630 lines
19 KiB
C++
630 lines
19 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: $
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// $Log: $
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================
|
||
|
|
||
|
|
||
|
#include<VGUI_LineBorder.h>
|
||
|
|
||
|
#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"
|
||
|
|
||
|
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(24)
|
||
|
#define SBOARD_HEADER_SIZE_Y YRES(40)
|
||
|
#define SBOARD_TABLE_X XRES(16)
|
||
|
#define SBOARD_TABLE_Y (SBOARD_TITLE_SIZE_Y + SBOARD_HEADER_SIZE_Y)
|
||
|
|
||
|
#define SBOARD_TEAM_CELL_SIZE_Y YRES(20)
|
||
|
#define SBOARD_CELL_SIZE_Y YRES(13)
|
||
|
|
||
|
// Column sizes
|
||
|
#define CSIZE_NAME XRES(140)
|
||
|
#define CSIZE_CLASS CSIZE_NAME + XRES(64)
|
||
|
#define CSIZE_KILLS CSIZE_CLASS + XRES(60)
|
||
|
#define CSIZE_DEATHS CSIZE_KILLS + XRES(64)
|
||
|
|
||
|
#define SMALL_CSIZE_NAME XRES(124)
|
||
|
#define SMALL_CSIZE_CLASS SMALL_CSIZE_NAME + XRES(60)
|
||
|
#define SMALL_CSIZE_KILLS SMALL_CSIZE_CLASS + XRES(64)
|
||
|
#define SMALL_CSIZE_DEATHS SMALL_CSIZE_KILLS + XRES(70)
|
||
|
|
||
|
#define TEAM_NO 0
|
||
|
#define TEAM_YES 1
|
||
|
#define TEAM_UNASSIGNED 2
|
||
|
#define TEAM_SPECTATORS 3
|
||
|
|
||
|
// Team Colors used in the scoreboard
|
||
|
int ScoreColorsBG[5][3] =
|
||
|
{
|
||
|
{ 0, 0, 0 },
|
||
|
{ 66, 114, 247 },
|
||
|
{ 220, 51, 38 },
|
||
|
{ 236, 212, 48 },
|
||
|
{ 68, 199, 42 },
|
||
|
};
|
||
|
|
||
|
int ScoreColorsFG[5][3] =
|
||
|
{
|
||
|
{ 255, 255, 255 },
|
||
|
{ 170, 193, 251 },
|
||
|
{ 215, 151, 146 },
|
||
|
{ 227, 203, 46 },
|
||
|
{ 143, 215, 142 },
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set different cell heights for Teams and Players
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int ScoreTablePanel::getCellTall(int row)
|
||
|
{
|
||
|
if ( m_iIsATeam[row] )
|
||
|
return SBOARD_TEAM_CELL_SIZE_Y;
|
||
|
|
||
|
return SBOARD_CELL_SIZE_Y;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Render each of the cells in the Table
|
||
|
//-----------------------------------------------------------------------------
|
||
|
Panel* ScoreTablePanel::getCellRenderer(int column,int row,bool columnSelected,bool rowSelected,bool cellSelected)
|
||
|
{
|
||
|
char sz[128];
|
||
|
hud_player_info_t *pl_info = NULL;
|
||
|
team_info_t *team_info = NULL;
|
||
|
|
||
|
if ( m_iIsATeam[row] == TEAM_YES )
|
||
|
{
|
||
|
// Get the team's data
|
||
|
team_info = &g_TeamInfo[ m_iSortedRows[row] ];
|
||
|
|
||
|
// White text for team names
|
||
|
m_pLabel->setFgColor(Scheme::sc_white);
|
||
|
|
||
|
// Set background color
|
||
|
m_pLabel->setBgColor( ScoreColorsBG[ team_info->teamnumber ][0], ScoreColorsBG[ team_info->teamnumber ][1], ScoreColorsBG[ team_info->teamnumber ][2], 128 );
|
||
|
}
|
||
|
else if ( m_iIsATeam[row] == TEAM_UNASSIGNED || m_iIsATeam[row] == TEAM_SPECTATORS )
|
||
|
{
|
||
|
// White text for team names
|
||
|
m_pLabel->setFgColor(Scheme::sc_white);
|
||
|
|
||
|
// Set background color
|
||
|
m_pLabel->setBgColor( 0,0,0, 255 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Grey text for player names
|
||
|
m_pLabel->setFgColor( ScoreColorsFG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][0], ScoreColorsFG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][1], ScoreColorsFG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][2], 128 );
|
||
|
|
||
|
// 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
|
||
|
m_pLabel->setFgColor(Scheme::sc_white);
|
||
|
m_pLabel->setBgColor( ScoreColorsBG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][0], ScoreColorsBG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][1], ScoreColorsBG[ g_PlayerExtraInfo[ m_iSortedRows[row] ].teamnumber ][2], 196 );
|
||
|
}
|
||
|
else if ( m_iSortedRows[row] == m_iLastKilledBy && m_fLastKillTime && m_fLastKillTime > gHUD.m_flTime )
|
||
|
{
|
||
|
// Killer's name
|
||
|
m_pLabel->setBgColor( 255,0,0, 255 - ((float)15 * (float)(m_fLastKillTime - gHUD.m_flTime)) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pLabel->setBgColor( 0,0,0, 255 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Align
|
||
|
if (column <= 1)
|
||
|
{
|
||
|
if ( m_iIsATeam[row] )
|
||
|
m_pLabel->setContentAlignment( vgui::Label::a_southwest );
|
||
|
else
|
||
|
m_pLabel->setContentAlignment( vgui::Label::a_west );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( m_iIsATeam[row] )
|
||
|
m_pLabel->setContentAlignment( vgui::Label::a_south );
|
||
|
else
|
||
|
m_pLabel->setContentAlignment( vgui::Label::a_center );
|
||
|
}
|
||
|
|
||
|
// Fill out with the correct data
|
||
|
if ( m_iIsATeam[row] )
|
||
|
{
|
||
|
int i;
|
||
|
char sz2[128];
|
||
|
strcpy(sz, "");
|
||
|
|
||
|
switch (column)
|
||
|
{
|
||
|
case 0:
|
||
|
if ( m_iIsATeam[row] == TEAM_UNASSIGNED )
|
||
|
{
|
||
|
sprintf( sz2, CHudTextMessage::BufferedLocaliseTextString( "#Unassigned" ) );
|
||
|
}
|
||
|
else if ( m_iIsATeam[row] == TEAM_SPECTATORS )
|
||
|
{
|
||
|
sprintf( sz2, CHudTextMessage::BufferedLocaliseTextString( "#Spectators" ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprintf( sz2, CHudTextMessage::BufferedLocaliseTextString( team_info->name ) );
|
||
|
}
|
||
|
|
||
|
// Uppercase it
|
||
|
for (i = 0; i < (int)strlen(sz2); i++)
|
||
|
{
|
||
|
if ( *(sz2 + i) )
|
||
|
sz[i] = toupper( *(sz2 + i) );
|
||
|
}
|
||
|
sz[i] = '\0';
|
||
|
|
||
|
// Append the number of players
|
||
|
if ( m_iIsATeam[row] == TEAM_YES )
|
||
|
{
|
||
|
if (team_info->players == 1)
|
||
|
sprintf(sz, "%s (1 player)", sz );
|
||
|
else
|
||
|
sprintf(sz, "%s (%d players)", sz, team_info->players );
|
||
|
}
|
||
|
break;
|
||
|
case 1:
|
||
|
// No class for teams
|
||
|
break;
|
||
|
case 2:
|
||
|
if ( m_iIsATeam[row] == TEAM_YES )
|
||
|
sprintf(sz, "%d", team_info->frags );
|
||
|
break;
|
||
|
case 3:
|
||
|
if ( m_iIsATeam[row] == TEAM_YES )
|
||
|
sprintf(sz, "%d", team_info->deaths );
|
||
|
break;
|
||
|
case 4:
|
||
|
if ( m_iIsATeam[row] == TEAM_YES )
|
||
|
sprintf(sz, "%d", team_info->ping );
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
m_pLabel->setText(sz);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool bShowClass = false;
|
||
|
|
||
|
switch (column)
|
||
|
{
|
||
|
case 0:
|
||
|
sprintf(sz, " %s", pl_info->name);
|
||
|
break;
|
||
|
case 1:
|
||
|
// 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 ( g_iUser1 )
|
||
|
bShowClass = true;
|
||
|
|
||
|
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)
|
||
|
sprintf(sz, "");
|
||
|
else
|
||
|
sprintf( sz, "%s", CHudTextMessage::BufferedLocaliseTextString( sLocalisedClasses[ g_PlayerExtraInfo[ m_iSortedRows[row] ].playerclass ] ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
strcpy(sz, "");
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
sprintf(sz, "%d", g_PlayerExtraInfo[ m_iSortedRows[row] ].frags );
|
||
|
break;
|
||
|
case 3:
|
||
|
sprintf(sz, "%d", g_PlayerExtraInfo[ m_iSortedRows[row] ].deaths );
|
||
|
break;
|
||
|
case 4:
|
||
|
sprintf(sz, "%d", g_PlayerInfoList[ m_iSortedRows[row] ].ping );
|
||
|
break;
|
||
|
default:
|
||
|
strcpy(sz, "");
|
||
|
}
|
||
|
|
||
|
m_pLabel->setText(sz);
|
||
|
}
|
||
|
|
||
|
return m_pLabel;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Create the ScoreBoard panel
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ScorePanel::ScorePanel(int x,int y,int wide,int tall) : Panel(x,y,wide,tall)
|
||
|
{
|
||
|
setBorder( new LineBorder( Color(255 * 0.7,170 * 0.7,0,0) ) );
|
||
|
setBgColor( 0,0,0, 100 );
|
||
|
|
||
|
m_pTitleLabel = new Label( " SCORES", 0,0, wide, SBOARD_TITLE_SIZE_Y );
|
||
|
m_pTitleLabel->setBgColor( Scheme::sc_primary2 );
|
||
|
m_pTitleLabel->setFgColor( Scheme::sc_primary1 );
|
||
|
m_pTitleLabel->setContentAlignment( vgui::Label::a_west );
|
||
|
m_pTitleLabel->setParent(this);
|
||
|
|
||
|
_headerPanel = new HeaderPanel( SBOARD_TABLE_X, SBOARD_TITLE_SIZE_Y, wide - (SBOARD_TABLE_X * 2), SBOARD_HEADER_SIZE_Y);
|
||
|
_headerPanel->setParent(this);
|
||
|
|
||
|
// BUGBUG: This isn't working. gHUD.m_Teamplay hasn't been initialized yet.
|
||
|
if ( gHUD.m_Teamplay )
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#TEAMS" ), true) );
|
||
|
else
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#PLAYERS" ), true) );
|
||
|
|
||
|
if ( HUD_IsGame( "tfc" ) )
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#CLASS" ), true) );
|
||
|
else
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader("", true) );
|
||
|
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#SCORE" )) );
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#DEATHS")) );
|
||
|
_headerPanel->addSectionPanel( new CLabelHeader( CHudTextMessage::BufferedLocaliseTextString( "#LATENCY")) );
|
||
|
_headerPanel->setBgColor( 0,0,0, 255 );
|
||
|
|
||
|
// Need to special case 400x300, because the titles just wont fit otherwise
|
||
|
if ( ScreenWidth == 400 )
|
||
|
{
|
||
|
_headerPanel->setSliderPos( 0, SMALL_CSIZE_NAME );
|
||
|
_headerPanel->setSliderPos( 1, SMALL_CSIZE_CLASS );
|
||
|
_headerPanel->setSliderPos( 2, SMALL_CSIZE_KILLS );
|
||
|
_headerPanel->setSliderPos( 3, SMALL_CSIZE_DEATHS );
|
||
|
_headerPanel->setSliderPos( 4, wide - (SBOARD_TABLE_X * 2) - 1 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_headerPanel->setSliderPos( 0, CSIZE_NAME );
|
||
|
_headerPanel->setSliderPos( 1, CSIZE_CLASS );
|
||
|
_headerPanel->setSliderPos( 2, CSIZE_KILLS );
|
||
|
_headerPanel->setSliderPos( 3, CSIZE_DEATHS );
|
||
|
_headerPanel->setSliderPos( 4, wide - (SBOARD_TABLE_X * 2) - 1 );
|
||
|
}
|
||
|
|
||
|
_tablePanel = new ScoreTablePanel(SBOARD_TABLE_X, SBOARD_TABLE_Y, wide - (SBOARD_TABLE_X * 2), tall - SBOARD_TABLE_Y, NUM_COLUMNS);
|
||
|
_tablePanel->setParent(this);
|
||
|
_tablePanel->setHeaderPanel(_headerPanel);
|
||
|
_tablePanel->setBgColor( 0,0,0, 255 );
|
||
|
|
||
|
Initialize();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Called each time a new level is started.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void ScorePanel::Initialize( void )
|
||
|
{
|
||
|
// Clear out scoreboard data
|
||
|
_tablePanel->m_iLastKilledBy = 0;
|
||
|
_tablePanel->m_fLastKillTime = 0;
|
||
|
m_iPlayerNum = 0;
|
||
|
m_iNumTeams = 0;
|
||
|
memset( g_PlayerExtraInfo, 0, sizeof g_PlayerExtraInfo );
|
||
|
memset( g_TeamInfo, 0, sizeof g_TeamInfo );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Set the size of the header and table when this panel's size changes
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void ScorePanel::setSize(int wide,int tall)
|
||
|
{
|
||
|
Panel::setSize(wide,tall);
|
||
|
|
||
|
_headerPanel->setBounds(0,0,wide,SBOARD_HEADER_SIZE_Y);
|
||
|
_tablePanel->setBounds(0,20,wide,tall - SBOARD_HEADER_SIZE_Y);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Recalculate the internal scoreboard data
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void ScorePanel::Update()
|
||
|
{
|
||
|
// Set the title
|
||
|
if (gViewPort->m_szServerName)
|
||
|
{
|
||
|
char sz[MAX_SERVERNAME_LENGTH + 16];
|
||
|
sprintf(sz, " SCORES: %s", gViewPort->m_szServerName );
|
||
|
m_pTitleLabel->setText(sz);
|
||
|
}
|
||
|
|
||
|
_tablePanel->m_iRows = 0;
|
||
|
gViewPort->GetAllPlayersInfo();
|
||
|
|
||
|
// Clear out sorts
|
||
|
for (int i = 0; i < MAX_PLAYERS; i++)
|
||
|
{
|
||
|
_tablePanel->m_iSortedRows[i] = 0;
|
||
|
_tablePanel->m_iIsATeam[i] = TEAM_NO;
|
||
|
_tablePanel->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();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Sort all the teams
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void ScorePanel::SortTeams()
|
||
|
{
|
||
|
// clear out team scores
|
||
|
for ( int 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 == 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].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
|
||
|
_tablePanel->m_iSortedRows[ _tablePanel->m_iRows ] = best_team;
|
||
|
_tablePanel->m_iIsATeam[ _tablePanel->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
|
||
|
_tablePanel->m_iRows++;
|
||
|
|
||
|
// Now sort all the players on this team
|
||
|
SortPlayers( 0, g_TeamInfo[best_team].name );
|
||
|
}
|
||
|
|
||
|
// Now add all the spectators
|
||
|
SortPlayers( TEAM_SPECTATORS, NULL );
|
||
|
|
||
|
// Now add all the players who aren't in a team yet
|
||
|
SortPlayers( TEAM_UNASSIGNED, 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 ( _tablePanel->m_bHasBeenSorted[i] == false && g_PlayerInfoList[i].name && g_PlayerExtraInfo[i].frags >= highest_frags )
|
||
|
{
|
||
|
cl_entity_t *ent = gEngfuncs.GetEntityByIndex( i );
|
||
|
|
||
|
if ( ent && ((iTeam == TEAM_SPECTATORS && g_IsSpectator[i] != 0) || (iTeam != TEAM_SPECTATORS && !(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)
|
||
|
{
|
||
|
_tablePanel->m_iIsATeam[ _tablePanel->m_iRows ] = iTeam;
|
||
|
_tablePanel->m_iRows++;
|
||
|
|
||
|
bCreatedTeam = true;
|
||
|
}
|
||
|
|
||
|
// Put this player in the sorted list
|
||
|
_tablePanel->m_iSortedRows[ _tablePanel->m_iRows ] = best_player;
|
||
|
_tablePanel->m_bHasBeenSorted[ best_player ] = true;
|
||
|
_tablePanel->m_iRows++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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 )
|
||
|
{
|
||
|
_tablePanel->m_iLastKilledBy = killer ? killer : m_iPlayerNum;
|
||
|
_tablePanel->m_fLastKillTime = gHUD.m_flTime + 10; // display who we were killed by for 10 seconds
|
||
|
|
||
|
if ( killer == m_iPlayerNum )
|
||
|
_tablePanel->m_iLastKilledBy = m_iPlayerNum;
|
||
|
}
|
||
|
}
|
||
|
|