sof2-sdk/code/cgame/cg_scoreboard.c

522 lines
14 KiB
C

// Copyright (C) 2001-2002 Raven Software.
//
// cg_scoreboard -- draw the scoreboard on top of the game screen
#include "cg_local.h"
#define SB_HEADER 86
#define SB_TOP (SB_HEADER+32)
// Where the status bar starts, so we don't overwrite it
#define SB_STATUSBAR 420
#define SB_MAXCLIENTS_NORMAL 12
#define SB_MAXCLIENTS_MORE 26
#define SB_MAXCLIENTS_ALOT 32
// Used when interleaved
#define SB_HEADER_WIDTH (580)
#define SB_HEADER_HEIGHT 30
#define SB_HEADER_X ((640-SB_HEADER_WIDTH)/2)
#define SB_HEADER_Y 86
#define SB_SCORELINE_X (SB_HEADER_X+30)
#define SB_SCORELINE_Y 120
#define SB_SCORELINE_WIDTH (SB_HEADER_WIDTH-60)
#define SB_NAME_X (SB_SCORELINE_X)
#define SB_SCORE_X (SB_SCORELINE_X+287)
#define SB_KILLS_X (SB_SCORELINE_X+335)
#define SB_DEATHS_X (SB_SCORELINE_X+384)
#define SB_PING_X (SB_SCORELINE_X+440)
#define SB_TIME_X (SB_SCORELINE_X+489)
int sb_lineHeight;
int sb_maxClients;
float sb_nameFontScale;
float sb_numberFontScale;
float sb_readyFontScale;
/*
=================
CG_DrawClientScore
=================
*/
static void CG_DrawClientScore( float x, float y, float w, score_t* score, float* color )
{
clientInfo_t* ci;
vec4_t dataColor;
vec4_t nameColor;
const char* s;
float f;
nameColor[3] = dataColor[3] = 1.0f;
// Validate the score
if ( score->client < 0 || score->client >= cgs.maxclients )
{
Com_Printf( "Bad score->client: %i\n", score->client );
return;
}
// Convienience
ci = &cgs.clientinfo[score->client];
CG_DrawPic ( x - 5, y, w, sb_lineHeight, cgs.media.scoreboardLine );
// highlight your position
if ( score->client == cg.snap->ps.clientNum )
{
vec4_t hcolor;
hcolor[0] = 1.0f;
hcolor[1] = 1.0f;
hcolor[2] = 1.0f;
hcolor[3] = .10f;
CG_FillRect( x - 5, y, w, sb_lineHeight, hcolor );
VectorSet ( nameColor, 1.0f, 1.0f, 1.0f );
VectorSet ( dataColor, 0.5f, 0.5f, 0.5f );
}
else if ( (cg.snap->ps.pm_type == PM_DEAD) && score->client == cg.snap->ps.persistant[PERS_ATTACKER] )
{
vec4_t hcolor;
hcolor[0] = 1.0f;
hcolor[1] = 1.0f;
hcolor[2] = 1.0f;
hcolor[3] = .10f;
CG_FillRect( x - 5, y, w, sb_lineHeight, hcolor );
VectorCopy ( color, nameColor );
VectorSet ( dataColor, 0.5f, 0.5f, 0.5f );
}
else
{
VectorCopy ( color, nameColor );
VectorSet ( dataColor, 0.3f, 0.3f, 0.3f );
if ( ci->ghost )
{
VectorScale ( nameColor, 0.6f, nameColor );
}
}
CG_DrawText( x, y, cgs.media.hudFont, sb_nameFontScale, nameColor, ci->name, 24, DT_OUTLINE );
s = va("%i", score->score );
f = trap_R_GetTextWidth ( s, cgs.media.hudFont, sb_nameFontScale, 0 );
CG_DrawText( x + w - 10 - f, y, cgs.media.hudFont, sb_nameFontScale, nameColor, va("%i", score->score), 0, DT_OUTLINE );
// Draw skull if dead and not in intermission
if ( cg.snap->ps.pm_type == PM_INTERMISSION )
{
if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) )
{
vec3_t deadColor;
deadColor[0] = 0.60f;
deadColor[1] = 0.60f;
deadColor[2] = 0.60f;
deadColor[3] = 1.0f;
CG_DrawText( x + w - 70, y + 3, cgs.media.hudFont, sb_readyFontScale, deadColor, "READY", 0, DT_OUTLINE );
}
}
else if ( ci->ghost )
{
CG_DrawPic ( x + w - 70, y + 1, sb_numberFontScale * 0.8f, sb_numberFontScale * 0.8f, cgs.media.deadShader );
}
// Draw any gametype items the guy is carrying
else
{
float xx = x + w - 70;
int i;
for ( i = 0; i < MAX_GAMETYPE_ITEMS; i ++ )
{
centity_t* cent;
cent = CG_GetEntity ( score->client);
// No have item, no draw it
if ( !(ci->gametypeitems & (1<<i) ) )
{
continue;
}
if ( !cg_items[MODELINDEX_GAMETYPE_ITEM+i].icon )
{
continue;
}
CG_DrawPic ( xx, y + 1, sb_numberFontScale * 0.8f, sb_numberFontScale * 0.8f, cg_items[MODELINDEX_GAMETYPE_ITEM+i].icon );
xx += sb_numberFontScale;
}
}
s = va("%i/%i", score->kills, score->deaths);
f = trap_R_GetTextWidth ( s, cgs.media.hudFont, sb_readyFontScale, 0 );
CG_DrawText( x + w - 10 - f, y + sb_numberFontScale, cgs.media.hudFont, sb_readyFontScale, dataColor, s, 0, 0 );
CG_DrawText( x, y + sb_numberFontScale, cgs.media.hudFont, sb_readyFontScale, dataColor, va("id: %i", score->client ), 0, 0 );
CG_DrawText( x + 40, y + sb_numberFontScale, cgs.media.hudFont, sb_readyFontScale, dataColor, va("ping: %i", score->ping ), 0, 0 );
CG_DrawText( x + 95, y + sb_numberFontScale, cgs.media.hudFont, sb_readyFontScale, dataColor, va("time: %i", score->time ), 0, 0 );
if ( score->teamkillDamage )
{
CG_DrawText( x + 150, y + sb_numberFontScale, cgs.media.hudFont, sb_readyFontScale, dataColor, va("tk: %i%%", score->teamkillDamage ), 0, 0 );
}
}
/*
=================
CG_TeamScoreboard
=================
*/
static int CG_TeamScoreboard( float x, float y, float w, team_t team )
{
int i;
int skipped;
float color[4];
int count;
clientInfo_t *ci;
const char* s;
int players;
qboolean drawnClient;
// Do we make sure the current client is drawn?
drawnClient = qtrue;
if ( cg.scores [ cg.snap->ps.clientNum ].team == team )
{
drawnClient = qfalse;
}
// Determine the color for this team
switch ( team )
{
case TEAM_RED:
VectorCopy4 ( g_color_table[ColorIndex(COLOR_RED)], color );
break;
case TEAM_BLUE:
VectorCopy4 ( g_color_table[ColorIndex(COLOR_BLUE)], color );
break;
case TEAM_FREE:
VectorCopy4 ( g_color_table[ColorIndex(COLOR_GREEN)], color );
break;
case TEAM_SPECTATOR:
default:
VectorCopy4 ( colorWhite, color );
break;
}
// Draw as many clients as we can for this team
for ( skipped = -1, count = 0, i = 0 ; i < cg.numScores && count < sb_maxClients ; i++ )
{
score_t* score;
score = &cg.scores[i];
ci = &cgs.clientinfo[ score->client ];
if ( team != score->team )
{
continue;
}
if ( count == sb_maxClients - 1 && !drawnClient )
{
if ( score->client != cg.snap->ps.clientNum )
{
skipped = i;
continue;
}
drawnClient = qtrue;
}
CG_DrawClientScore( x, y + SB_HEADER_HEIGHT + sb_lineHeight * count, w, score, color );
count++;
}
if ( skipped != -1 && count < sb_maxClients )
{
CG_DrawClientScore( x, y + SB_HEADER_HEIGHT + sb_lineHeight * count, w, &cg.scores[skipped], color );
count++;
}
s = "";
switch ( team )
{
case TEAM_RED:
s = "RED TEAM";
players = ui_info_redcount.integer;
break;
case TEAM_BLUE:
s = "BLUE TEAM";
players = ui_info_bluecount.integer;
break;
case TEAM_FREE:
s = "PLAYERS";
players = ui_info_freecount.integer;
break;
default:
case TEAM_SPECTATOR:
s = "SPECTATORS";
players = ui_info_speccount.integer;
break;
}
// Use the same team color here, but alpha it a bit.
color[3] = 0.6f;
// Draw the header information for this team
CG_DrawPic ( x - 5, y, w, 25, cgs.media.scoreboardHeader );
CG_FillRect( x - 5, y, w, 25, color );
CG_DrawText ( x, y, cgs.media.hudFont, 0.40f, colorWhite, va("%s", s), 0, 0 );
CG_DrawText ( x, y + 13, cgs.media.hudFont, 0.30f, colorWhite, va("players: %d", players), 0, 0 );
// Draw the totals if this is the red or blue team
if ( (team == TEAM_RED || team == TEAM_BLUE))
{
const char* s;
float f;
s = va("%d",(cg.teamScores[team-TEAM_RED]));
f = trap_R_GetTextWidth ( s, cgs.media.hudFont, 0.43f, 0 );
CG_DrawText ( x + w - 10 - f, y, cgs.media.hudFont, 0.43f, colorWhite, s, 0, DT_OUTLINE );
}
if ( count )
{
CG_DrawPic ( x - 5, y + SB_HEADER_HEIGHT + sb_lineHeight * count, w, sb_lineHeight, cgs.media.scoreboardFooter );
}
y = count * sb_lineHeight + y + 10;
if ( y > cg.scoreBoardBottom )
{
cg.scoreBoardBottom = y;
}
return y;
}
/*
=================
CG_DrawNormalScoreboard
Draws a scoreboard that has no teams
=================
*/
qboolean CG_DrawNormalScoreboard ( float y )
{
cg.scoreBoardBottom = 0;
// DRaw the game timer and the game type
CG_DrawText ( 385, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, "Game Time:", 0, DT_OUTLINE );
CG_DrawTimer ( 455, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, DT_OUTLINE, cg.time - cgs.levelStartTime );
CG_DrawText ( 150, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, cgs.gametypeData->displayName, 0, DT_OUTLINE );
if ( ui_info_speccount.integer )
{
CG_FillRect ( 0, 470, 640, 10, colorBlack );
CG_DrawText ( 5, 470, cgs.media.hudFont, 0.30f, colorWhite, va("SPECTATORS: %s", cg.scoreBoardSpectators), 0, 0 );
}
if ( ui_info_freecount.integer > 10 )
{
sb_maxClients = 16;
sb_lineHeight = 20;
sb_nameFontScale = 0.35f;
sb_readyFontScale = 0.30f;
sb_numberFontScale = trap_R_GetTextHeight ( "W", cgs.media.hudFont, sb_nameFontScale, 0 );
}
else
{
sb_maxClients = 10;
sb_lineHeight = 30;
sb_nameFontScale = 0.43f;
sb_readyFontScale = 0.30f;
sb_numberFontScale = trap_R_GetTextHeight ( "W", cgs.media.hudFont, sb_nameFontScale, 0 ) + 4;
}
CG_TeamScoreboard ( 150, y, 350, TEAM_FREE );
return qtrue;
}
/*
=================
CG_DrawTeamScoreboard
Draw the normal in-game scoreboard
=================
*/
qboolean CG_DrawTeamScoreboard( float y )
{
qboolean redFirst = qfalse;
cg.scoreBoardBottom = 0;
// DRaw the game timer and the game type
CG_DrawText ( 470, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, "Game Time:", 0, DT_OUTLINE );
CG_DrawTimer ( 540, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, DT_OUTLINE, cg.time - cgs.levelStartTime );
CG_DrawText ( 60, y - 14, cgs.media.hudFont, 0.38f, colorLtGrey, cgs.gametypeData->displayName, 0, DT_OUTLINE );
if ( ui_info_speccount.integer )
{
CG_FillRect ( 0, 470, 640, 10, colorBlack );
CG_DrawText ( 5, 470, cgs.media.hudFont, 0.30f, colorWhite, va("SPECTATORS: %s", cg.scoreBoardSpectators), 0, 0 );
}
if ( ui_info_redcount.integer > 10 || ui_info_bluecount.integer > 10 )
{
sb_maxClients = 16;
sb_lineHeight = 20;
sb_nameFontScale = 0.35f;
sb_readyFontScale = 0.30f;
sb_numberFontScale = trap_R_GetTextHeight ( "W", cgs.media.hudFont, sb_nameFontScale, 0 );
}
else
{
sb_maxClients = 10;
sb_lineHeight = 30;
sb_nameFontScale = 0.43f;
sb_readyFontScale = 0.30f;
sb_numberFontScale = trap_R_GetTextHeight ( "W", cgs.media.hudFont, sb_nameFontScale, 0 ) + 4;
}
// If there are more scores than the scoreboard can show then show the
// players team first rather than the winning team
if ( cgs.clientinfo[cg.clientNum].team == TEAM_SPECTATOR )
{
if ( cg.teamScores[0] >= cg.teamScores[1] )
{
redFirst = qtrue;
}
}
else if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED )
{
redFirst = qtrue;
}
if ( redFirst )
{
CG_TeamScoreboard( 50, y, 265, TEAM_RED );
CG_TeamScoreboard( 330, y, 265, TEAM_BLUE );
}
else
{
CG_TeamScoreboard( 330, y, 265, TEAM_RED );
CG_TeamScoreboard( 50, y, 265, TEAM_BLUE );
}
return qtrue;
}
/*
=================
CG_DrawScoreboard
Draws either a team or a normal scoreboard
=================
*/
qboolean CG_DrawScoreboard ( void )
{
float y;
float w;
// don't draw amuthing if the menu or console is up
if ( cg_paused.integer )
{
cg.deferredPlayerLoading = 0;
return qfalse;
}
// don't draw scoreboard during death while warmup up
if ( cg.warmup && !cg.showScores )
{
return qfalse;
}
if ( !cg.showScores &&
cg.predictedPlayerState.pm_type != PM_DEAD &&
cg.predictedPlayerState.pm_type != PM_INTERMISSION )
{
return qfalse;
}
// scoreboard
y = 45;
if ( cgs.gametypeData->teams )
{
if ( ui_info_redcount.integer < 10 && ui_info_bluecount.integer < 10 )
{
y += 50;
}
}
else if ( ui_info_freecount.integer < 10 )
{
y += 50;
}
// Draw any gameover text
if ( cgs.gameover[0] )
{
w = trap_R_GetTextWidth ( cgs.gameover, cgs.media.hudFont, 0.48f, 0 );
CG_DrawText ( 320 - w / 2, y - 30, cgs.media.hudFont, 0.48f, colorWhite, cgs.gameover, 0, DT_OUTLINE );
}
else if ( cgs.gametypeMessageTime > cg.time )
{
w = trap_R_GetTextWidth ( cgs.gametypeMessage, cgs.media.hudFont, 0.48f, 0 );
CG_DrawText ( 320 - w / 2, y - 30, cgs.media.hudFont, 0.48f, colorWhite, cgs.gametypeMessage, 0, DT_OUTLINE );
}
// Should we draw who killed you?
else if ( cg.snap->ps.pm_type == PM_DEAD &&
cg.snap->ps.persistant[PERS_ATTACKER] < MAX_CLIENTS &&
cg.snap->ps.persistant[PERS_ATTACKER] != cg.snap->ps.clientNum )
{
const char* s;
s = va("Killed by %s", cgs.clientinfo[cg.snap->ps.persistant[PERS_ATTACKER]].name );
w = trap_R_GetTextWidth ( s, cgs.media.hudFont, 0.48f, 0 );
CG_DrawText ( 320 - w / 2, y - 30, cgs.media.hudFont, 0.48f, colorWhite, s, 0, DT_OUTLINE );
}
else if ( cgs.gametypeData->teams )
{
const char* s;
if ( cg.teamScores[0] == cg.teamScores[1] )
{
s = va ( "Game Tied at %d", cg.teamScores[0] );
}
else if ( cg.teamScores[0] > cg.teamScores[1] )
{
s = va ( "Red leads Blue by %d", cg.teamScores[0] - cg.teamScores[1] );
}
else
{
s = va ( "Blue leads Red by %d", cg.teamScores[1] - cg.teamScores[0] );
}
w = trap_R_GetTextWidth ( s, cgs.media.hudFont, 0.48f, 0 );
CG_DrawText ( 320 - w / 2, y - 30, cgs.media.hudFont, 0.48f, colorWhite, s, 0, DT_OUTLINE );
}
// load any models that have been deferred
cg.deferredPlayerLoading++;
if ( cgs.gametypeData->teams )
{
return CG_DrawTeamScoreboard ( y );
}
return CG_DrawNormalScoreboard ( y );
}