ioq3/code/cgame/cg_scoreboard.c
Zack Middleton 082376ed9e Enable tourney scoreboard in Team Arena
"/team score" draws an oversized scoreboard in Q3. In Team Arena
it draws nothing. They probably intended to replace it with the
new .menu UI. But since it didn't happen, go ahead and use the Q3
tournament scoreboard.
2017-06-02 22:11:52 -05:00

534 lines
15 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
// cg_scoreboard -- draw the scoreboard on top of the game screen
#include "cg_local.h"
#define SCOREBOARD_X (0)
#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_NORMAL_HEIGHT 40
#define SB_INTER_HEIGHT 16 // interleaved height
#define SB_MAXCLIENTS_NORMAL ((SB_STATUSBAR - SB_TOP) / SB_NORMAL_HEIGHT)
#define SB_MAXCLIENTS_INTER ((SB_STATUSBAR - SB_TOP) / SB_INTER_HEIGHT - 1)
// Used when interleaved
#define SB_LEFT_BOTICON_X (SCOREBOARD_X+0)
#define SB_LEFT_HEAD_X (SCOREBOARD_X+32)
#define SB_RIGHT_BOTICON_X (SCOREBOARD_X+64)
#define SB_RIGHT_HEAD_X (SCOREBOARD_X+96)
// Normal
#define SB_BOTICON_X (SCOREBOARD_X+32)
#define SB_HEAD_X (SCOREBOARD_X+64)
#define SB_SCORELINE_X 112
#define SB_RATING_WIDTH (6 * BIGCHAR_WIDTH) // width 6
#define SB_SCORE_X (SB_SCORELINE_X + BIGCHAR_WIDTH) // width 6
#define SB_RATING_X (SB_SCORELINE_X + 6 * BIGCHAR_WIDTH) // width 6
#define SB_PING_X (SB_SCORELINE_X + 12 * BIGCHAR_WIDTH + 8) // width 5
#define SB_TIME_X (SB_SCORELINE_X + 17 * BIGCHAR_WIDTH + 8) // width 5
#define SB_NAME_X (SB_SCORELINE_X + 22 * BIGCHAR_WIDTH) // width 15
// The new and improved score board
//
// In cases where the number of clients is high, the score board heads are interleaved
// here's the layout
//
// 0 32 80 112 144 240 320 400 <-- pixel position
// bot head bot head score ping time name
//
// wins/losses are drawn on bot icon now
static qboolean localClient; // true if local client has been displayed
/*
=================
CG_DrawScoreboard
=================
*/
static void CG_DrawClientScore( int y, score_t *score, float *color, float fade, qboolean largeFormat ) {
char string[1024];
vec3_t headAngles;
clientInfo_t *ci;
int iconx, headx;
if ( score->client < 0 || score->client >= cgs.maxclients ) {
Com_Printf( "Bad score->client: %i\n", score->client );
return;
}
ci = &cgs.clientinfo[score->client];
iconx = SB_BOTICON_X + (SB_RATING_WIDTH / 2);
headx = SB_HEAD_X + (SB_RATING_WIDTH / 2);
// draw the handicap or bot skill marker (unless player has flag)
if ( ci->powerups & ( 1 << PW_NEUTRALFLAG ) ) {
if( largeFormat ) {
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_FREE, qfalse );
}
else {
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_FREE, qfalse );
}
} else if ( ci->powerups & ( 1 << PW_REDFLAG ) ) {
if( largeFormat ) {
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_RED, qfalse );
}
else {
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_RED, qfalse );
}
} else if ( ci->powerups & ( 1 << PW_BLUEFLAG ) ) {
if( largeFormat ) {
CG_DrawFlagModel( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, TEAM_BLUE, qfalse );
}
else {
CG_DrawFlagModel( iconx, y, 16, 16, TEAM_BLUE, qfalse );
}
} else {
if ( ci->botSkill > 0 && ci->botSkill <= 5 ) {
if ( cg_drawIcons.integer ) {
if( largeFormat ) {
CG_DrawPic( iconx, y - ( 32 - BIGCHAR_HEIGHT ) / 2, 32, 32, cgs.media.botSkillShaders[ ci->botSkill - 1 ] );
}
else {
CG_DrawPic( iconx, y, 16, 16, cgs.media.botSkillShaders[ ci->botSkill - 1 ] );
}
}
} else if ( ci->handicap < 100 ) {
Com_sprintf( string, sizeof( string ), "%i", ci->handicap );
if ( cgs.gametype == GT_TOURNAMENT )
CG_DrawSmallStringColor( iconx, y - SMALLCHAR_HEIGHT/2, string, color );
else
CG_DrawSmallStringColor( iconx, y, string, color );
}
// draw the wins / losses
if ( cgs.gametype == GT_TOURNAMENT ) {
Com_sprintf( string, sizeof( string ), "%i/%i", ci->wins, ci->losses );
if( ci->handicap < 100 && !ci->botSkill ) {
CG_DrawSmallStringColor( iconx, y + SMALLCHAR_HEIGHT/2, string, color );
}
else {
CG_DrawSmallStringColor( iconx, y, string, color );
}
}
}
// draw the face
VectorClear( headAngles );
headAngles[YAW] = 180;
if( largeFormat ) {
CG_DrawHead( headx, y - ( ICON_SIZE - BIGCHAR_HEIGHT ) / 2, ICON_SIZE, ICON_SIZE,
score->client, headAngles );
}
else {
CG_DrawHead( headx, y, 16, 16, score->client, headAngles );
}
#ifdef MISSIONPACK
// draw the team task
if ( ci->teamTask != TEAMTASK_NONE ) {
if ( ci->teamTask == TEAMTASK_OFFENSE ) {
CG_DrawPic( headx + 48, y, 16, 16, cgs.media.assaultShader );
}
else if ( ci->teamTask == TEAMTASK_DEFENSE ) {
CG_DrawPic( headx + 48, y, 16, 16, cgs.media.defendShader );
}
}
#endif
// draw the score line
if ( score->ping == -1 ) {
Com_sprintf(string, sizeof(string),
" connecting %s", ci->name);
} else if ( ci->team == TEAM_SPECTATOR ) {
Com_sprintf(string, sizeof(string),
" SPECT %3i %4i %s", score->ping, score->time, ci->name);
} else {
Com_sprintf(string, sizeof(string),
"%5i %4i %4i %s", score->score, score->ping, score->time, ci->name);
}
// highlight your position
if ( score->client == cg.snap->ps.clientNum ) {
float hcolor[4];
int rank;
localClient = qtrue;
if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR
|| cgs.gametype >= GT_TEAM ) {
rank = -1;
} else {
rank = cg.snap->ps.persistant[PERS_RANK] & ~RANK_TIED_FLAG;
}
if ( rank == 0 ) {
hcolor[0] = 0;
hcolor[1] = 0;
hcolor[2] = 0.7f;
} else if ( rank == 1 ) {
hcolor[0] = 0.7f;
hcolor[1] = 0;
hcolor[2] = 0;
} else if ( rank == 2 ) {
hcolor[0] = 0.7f;
hcolor[1] = 0.7f;
hcolor[2] = 0;
} else {
hcolor[0] = 0.7f;
hcolor[1] = 0.7f;
hcolor[2] = 0.7f;
}
hcolor[3] = fade * 0.7;
CG_FillRect( SB_SCORELINE_X + BIGCHAR_WIDTH + (SB_RATING_WIDTH / 2), y,
640 - SB_SCORELINE_X - BIGCHAR_WIDTH, BIGCHAR_HEIGHT+1, hcolor );
}
CG_DrawBigString( SB_SCORELINE_X + (SB_RATING_WIDTH / 2), y, string, fade );
// add the "ready" marker for intermission exiting
if ( cg.snap->ps.stats[ STAT_CLIENTS_READY ] & ( 1 << score->client ) ) {
CG_DrawBigStringColor( iconx, y, "READY", color );
}
}
/*
=================
CG_TeamScoreboard
=================
*/
static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, int lineHeight ) {
int i;
score_t *score;
float color[4];
int count;
clientInfo_t *ci;
color[0] = color[1] = color[2] = 1.0;
color[3] = fade;
count = 0;
for ( i = 0 ; i < cg.numScores && count < maxClients ; i++ ) {
score = &cg.scores[i];
ci = &cgs.clientinfo[ score->client ];
if ( team != ci->team ) {
continue;
}
CG_DrawClientScore( y + lineHeight * count, score, color, fade, lineHeight == SB_NORMAL_HEIGHT );
count++;
}
return count;
}
/*
=================
CG_DrawScoreboard
Draw the normal in-game scoreboard
=================
*/
qboolean CG_DrawOldScoreboard( void ) {
int x, y, w, i, n1, n2;
float fade;
float *fadeColor;
char *s;
int maxClients;
int lineHeight;
int topBorderSize, bottomBorderSize;
// don't draw amuthing if the menu or console is up
if ( cg_paused.integer ) {
cg.deferredPlayerLoading = 0;
return qfalse;
}
if ( cgs.gametype == GT_SINGLE_PLAYER && cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
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 ) {
fade = 1.0;
fadeColor = colorWhite;
} else {
fadeColor = CG_FadeColor( cg.scoreFadeTime, FADE_TIME );
if ( !fadeColor ) {
// next time scoreboard comes up, don't print killer
cg.deferredPlayerLoading = 0;
cg.killerName[0] = 0;
return qfalse;
}
fade = *fadeColor;
}
// fragged by ... line
if ( cg.killerName[0] ) {
s = va("Fragged by %s", cg.killerName );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
x = ( SCREEN_WIDTH - w ) / 2;
y = 40;
CG_DrawBigString( x, y, s, fade );
}
// current rank
if ( cgs.gametype < GT_TEAM) {
if (cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR ) {
s = va("%s place with %i",
CG_PlaceString( cg.snap->ps.persistant[PERS_RANK] + 1 ),
cg.snap->ps.persistant[PERS_SCORE] );
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
x = ( SCREEN_WIDTH - w ) / 2;
y = 60;
CG_DrawBigString( x, y, s, fade );
}
} else {
if ( cg.teamScores[0] == cg.teamScores[1] ) {
s = va("Teams are tied at %i", cg.teamScores[0] );
} else if ( cg.teamScores[0] >= cg.teamScores[1] ) {
s = va("Red leads %i to %i",cg.teamScores[0], cg.teamScores[1] );
} else {
s = va("Blue leads %i to %i",cg.teamScores[1], cg.teamScores[0] );
}
w = CG_DrawStrlen( s ) * BIGCHAR_WIDTH;
x = ( SCREEN_WIDTH - w ) / 2;
y = 60;
CG_DrawBigString( x, y, s, fade );
}
// scoreboard
y = SB_HEADER;
CG_DrawPic( SB_SCORE_X + (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardScore );
CG_DrawPic( SB_PING_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardPing );
CG_DrawPic( SB_TIME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardTime );
CG_DrawPic( SB_NAME_X - (SB_RATING_WIDTH / 2), y, 64, 32, cgs.media.scoreboardName );
y = SB_TOP;
// If there are more than SB_MAXCLIENTS_NORMAL, use the interleaved scores
if ( cg.numScores > SB_MAXCLIENTS_NORMAL ) {
maxClients = SB_MAXCLIENTS_INTER;
lineHeight = SB_INTER_HEIGHT;
topBorderSize = 8;
bottomBorderSize = 16;
} else {
maxClients = SB_MAXCLIENTS_NORMAL;
lineHeight = SB_NORMAL_HEIGHT;
topBorderSize = 16;
bottomBorderSize = 16;
}
localClient = qfalse;
if ( cgs.gametype >= GT_TEAM ) {
//
// teamplay scoreboard
//
y += lineHeight/2;
if ( cg.teamScores[0] >= cg.teamScores[1] ) {
n1 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight );
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED );
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
maxClients -= n1;
n2 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight );
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
maxClients -= n2;
} else {
n1 = CG_TeamScoreboard( y, TEAM_BLUE, fade, maxClients, lineHeight );
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n1 * lineHeight + bottomBorderSize, 0.33f, TEAM_BLUE );
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
maxClients -= n1;
n2 = CG_TeamScoreboard( y, TEAM_RED, fade, maxClients, lineHeight );
CG_DrawTeamBackground( 0, y - topBorderSize, 640, n2 * lineHeight + bottomBorderSize, 0.33f, TEAM_RED );
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
maxClients -= n2;
}
n1 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients, lineHeight );
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
} else {
//
// free for all scoreboard
//
n1 = CG_TeamScoreboard( y, TEAM_FREE, fade, maxClients, lineHeight );
y += (n1 * lineHeight) + BIGCHAR_HEIGHT;
n2 = CG_TeamScoreboard( y, TEAM_SPECTATOR, fade, maxClients - n1, lineHeight );
y += (n2 * lineHeight) + BIGCHAR_HEIGHT;
}
if (!localClient) {
// draw local client at the bottom
for ( i = 0 ; i < cg.numScores ; i++ ) {
if ( cg.scores[i].client == cg.snap->ps.clientNum ) {
CG_DrawClientScore( y, &cg.scores[i], fadeColor, fade, lineHeight == SB_NORMAL_HEIGHT );
break;
}
}
}
// load any models that have been deferred
if ( ++cg.deferredPlayerLoading > 10 ) {
CG_LoadDeferredPlayers();
}
return qtrue;
}
//================================================================================
/*
================
CG_CenterGiantLine
================
*/
static void CG_CenterGiantLine( float y, const char *string ) {
float x;
vec4_t color;
color[0] = 1;
color[1] = 1;
color[2] = 1;
color[3] = 1;
x = 0.5 * ( 640 - GIANT_WIDTH * CG_DrawStrlen( string ) );
CG_DrawStringExt( x, y, string, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
}
/*
=================
CG_DrawTourneyScoreboard
Draw the oversize scoreboard for tournements
=================
*/
void CG_DrawTourneyScoreboard( void ) {
const char *s;
vec4_t color;
int min, tens, ones;
clientInfo_t *ci;
int y;
int i;
// request more scores regularly
if ( cg.scoresRequestTime + 2000 < cg.time ) {
cg.scoresRequestTime = cg.time;
trap_SendClientCommand( "score" );
}
// draw the dialog background
color[0] = color[1] = color[2] = 0;
color[3] = 1;
CG_FillRect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, color );
color[0] = 1;
color[1] = 1;
color[2] = 1;
color[3] = 1;
// print the mesage of the day
s = CG_ConfigString( CS_MOTD );
if ( !s[0] ) {
s = "Scoreboard";
}
// print optional title
CG_CenterGiantLine( 8, s );
// print server time
ones = cg.time / 1000;
min = ones / 60;
ones %= 60;
tens = ones / 10;
ones %= 10;
s = va("%i:%i%i", min, tens, ones );
CG_CenterGiantLine( 64, s );
// print the two scores
y = 160;
if ( cgs.gametype >= GT_TEAM ) {
//
// teamplay scoreboard
//
CG_DrawStringExt( 8, y, "Red Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
s = va("%i", cg.teamScores[0] );
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
y += 64;
CG_DrawStringExt( 8, y, "Blue Team", color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
s = va("%i", cg.teamScores[1] );
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
} else {
//
// free for all scoreboard
//
for ( i = 0 ; i < MAX_CLIENTS ; i++ ) {
ci = &cgs.clientinfo[i];
if ( !ci->infoValid ) {
continue;
}
if ( ci->team != TEAM_FREE ) {
continue;
}
CG_DrawStringExt( 8, y, ci->name, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
s = va("%i", ci->score );
CG_DrawStringExt( 632 - GIANT_WIDTH * strlen(s), y, s, color, qtrue, qtrue, GIANT_WIDTH, GIANT_HEIGHT, 0 );
y += 64;
}
}
}