/* Copyright (C) 1997-2001 Id Software, Inc. This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cg_scoreboard.c -- fixed scoreboard layouts for gametypes #include "cg_local.h" extern cvar_t *cg_scoreboardStats; extern cvar_t *cg_scoreboardFont; extern cvar_t *cg_scoreboardWidthScale; vec4_t whiteTransparent = { 1.0f, 1.0f, 1.0f, 0.5f }; vec4_t colorSCBBackground = { 0.25f, 0.25f, 0.25f, 1.0f }; #define SCB_BACKGROUND_ALPHA 0.25f #define SCB_TEAMNAME_PIXELWIDTH (128 * cg_scoreboardWidthScale->value) #define SCB_PLAYERNAME_PIXELWIDTH (96 * cg_scoreboardWidthScale->value) #define SCB_MEDIUMFIELD_PIXELWIDTH (72 * cg_scoreboardWidthScale->value) #define SCB_SMALLFIELD_PIXELWIDTH (36 * cg_scoreboardWidthScale->value) #define SCB_TINYFIELD_PIXELWIDTH (24 * cg_scoreboardWidthScale->value) int SCR_ParseValue( char **s ); /* ================ SCR_DrawBigNumbersString ================ */ void SCR_DrawBigNumbersString( int x, int y, int fontwidth, int fontheight, char *string, vec4_t color ) { unsigned int i; int num; if( !string || !string[0] ) return; for( i=0; i 9 ) num = 0; } trap_R_DrawStretchPic ( x + (fontwidth*i), y, fontwidth, fontheight, 0, 0, 1, 1, color, CG_MediaShader(cgs.media.sbNums[num]) ); } } /* ================ SCR_PingColor print pings -> ping colors : 0-49 = green, 50-99 = yellow, 100+ = red ================ */ vec4_t *SCR_SetPingColor( int ping ) { if ( ping >= 0 && ping < 50 ) return &colorGreen; else if ( ping >= 50 && ping < 90 ) return &colorYellow; else if ( ping >= 90 && ping < 120 ) return &colorOrange; else return &colorRed; } //=============================================================== // // GENERIC SCOREBOARD TABS // //=============================================================== // it handles all the information for all gametypes, but not all of them // will set every field of it. typedef enum { SCBTAB_PLAYERFFA, SCBTAB_PLAYERRACE, SCBTAB_PLAYERDUEL, SCBTAB_PLAYERTDM, SCBTAB_PLAYERCTF, SCBTAB_SPECTATOR, SCBTAB_CHALLENGER, SCBTAB_CONNECTING, MAX_SCBTABS }scbtype_t; typedef struct { scbtype_t type; int playernum; int score; int ping; int kills; int deaths; int suicides; int teamfrags; int team; int ready; qboolean waiting; int min; int sec; int msec; }scb_playertab_t; static scb_playertab_t scb_players[MAX_CLIENTS]; static int scb_playercount; // player stats static int scb_player_stats[2*(WEAP_TOTAL-WEAP_GUNBLADE)]; // weak strong //================ //SCB_ColorForPlayer //================ static vec4_t playerColorTemp; float *SCB_ColorForPlayer( scb_playertab_t *player ) { if( player->team && player->team >= TEAM_RED ) { GS_TeamColor( player->team, playerColorTemp ); } else { VectorCopy( colorSCBBackground, playerColorTemp ); } //set alpha if( player->playernum == cg.chasedNum ) { playerColorTemp[3] = 0.8f; } else { playerColorTemp[3] = SCB_BACKGROUND_ALPHA; } return playerColorTemp; } void SCB_ParsePlayerStats( char **s ) { if( s && *s ) { int i,j; memset( scb_player_stats, 0, sizeof(scb_player_stats) ); j=0; for( i = WEAP_GUNBLADE; i < WEAP_TOTAL; i++ ) { // weak if( i == WEAP_LASERGUN || i == WEAP_ELECTROBOLT ) { scb_player_stats[j] = SCR_ParseValue( s ); } else { scb_player_stats[j] = -1; } j++; // strong if( i != WEAP_SHOCKWAVE ) scb_player_stats[j] = SCR_ParseValue( s ); else scb_player_stats[j] = -1; j++; } } } void SCB_ParseFFAPlayerTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_PLAYERFFA; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].score = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].ready = SCR_ParseValue( s ); scb_playercount++; } } void SCB_ParseRACEPlayerTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_PLAYERRACE; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].min = SCR_ParseValue( s ); scb_players[scb_playercount].sec = SCR_ParseValue( s ); scb_players[scb_playercount].msec = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].ready = SCR_ParseValue( s ); scb_playercount++; } } void SCB_ParseDUELPlayerTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_PLAYERDUEL; scb_players[scb_playercount].team = SCR_ParseValue( s ); scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].score = SCR_ParseValue( s ); scb_players[scb_playercount].kills = SCR_ParseValue( s ); scb_players[scb_playercount].deaths = SCR_ParseValue( s ); scb_players[scb_playercount].suicides = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_playercount++; } } void SCB_ParseTDMPlayerTab( char **s, int currentTeam ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_PLAYERTDM; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].score = SCR_ParseValue( s ); scb_players[scb_playercount].kills = SCR_ParseValue( s ); scb_players[scb_playercount].deaths = SCR_ParseValue( s ); scb_players[scb_playercount].suicides = SCR_ParseValue( s ); scb_players[scb_playercount].teamfrags = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].ready = SCR_ParseValue( s ); scb_players[scb_playercount].team = currentTeam; scb_playercount++; } } void SCB_ParseCTFPlayerTab( char **s, int currentTeam ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_PLAYERCTF; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].score = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].ready = SCR_ParseValue( s ); scb_players[scb_playercount].team = currentTeam; scb_playercount++; } } void SCB_ParseSpectatorTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_SPECTATOR; scb_players[scb_playercount].team = TEAM_SPECTATOR; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].waiting = qfalse; scb_playercount++; } } void SCB_ParseConnectingPlayerTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_CONNECTING; scb_players[scb_playercount].team = TEAM_SPECTATOR; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].ping = 0; scb_players[scb_playercount].waiting = qfalse; scb_playercount++; } } void SCB_ParseChallengerTab( char **s ) { if( s && *s ) { memset( &scb_players[scb_playercount], 0, sizeof(scb_playertab_t) ); scb_players[scb_playercount].type = SCBTAB_CHALLENGER; scb_players[scb_playercount].team = TEAM_SPECTATOR; scb_players[scb_playercount].playernum = SCR_ParseValue( s ); scb_players[scb_playercount].ping = SCR_ParseValue( s ); scb_players[scb_playercount].waiting = qtrue; scb_playercount++; } } #define SCB_FFA_PLAYERTAB_PIXELWIDTH (SCB_PLAYERNAME_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_MEDIUMFIELD_PIXELWIDTH) int SCB_DrawFFAPlayerTab( scb_playertab_t *player, int x, int y, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; int xoffset = 0; vec4_t *pingcolor; size_t len; //draw the box trap_R_DrawStretchPic( x + xoffset, y, SCB_FFA_PLAYERTAB_PIXELWIDTH, //width trap_SCR_strHeight( font ), //height 0, 0, 1, 1, SCB_ColorForPlayer(player), cgs.shaderWhite ); //print name Q_snprintfz( string, sizeof(string), "%s", cgs.clientInfo[player->playernum].name ); len = trap_SCR_StrlenForWidth( string, font, SCB_PLAYERNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( x+xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_PLAYERNAME_PIXELWIDTH; //print score Q_snprintfz( string, sizeof(string), "%3i", player->score ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorYellow ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ping Q_snprintfz( string, sizeof(string), "%3i", player->ping ); pingcolor = SCR_SetPingColor( player->ping ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, *pingcolor ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ready Q_snprintfz( string, sizeof(string), "%s", (cg.frame.match.state == MATCH_STATE_WARMUP && player->ready) ? "READY" : "" ); len = trap_SCR_StrlenForWidth( string, font, SCB_MEDIUMFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorGreen ); //xoffset += SCB_MEDIUMFIELD_PIXELWIDTH; return trap_SCR_strHeight( font ); } #define SCB_RACE_PLAYERTAB_PIXELWIDTH (SCB_PLAYERNAME_PIXELWIDTH+SCB_MEDIUMFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_MEDIUMFIELD_PIXELWIDTH) int SCB_DrawRACEPlayerTab( scb_playertab_t *player, int x, int y, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; vec4_t *pingcolor; int len, xoffset = 0; //draw the box trap_R_DrawStretchPic( x + xoffset, y, SCB_RACE_PLAYERTAB_PIXELWIDTH, //width trap_SCR_strHeight( font ), //height 0, 0, 1, 1, SCB_ColorForPlayer(player), cgs.shaderWhite ); //print name Q_snprintfz( string, sizeof(string), "%s", cgs.clientInfo[player->playernum].name ); len = trap_SCR_StrlenForWidth( string, font, SCB_PLAYERNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( x, y, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_PLAYERNAME_PIXELWIDTH; //print best lap time Q_snprintfz( string, sizeof(string),va("%02i:%02i.%1i", player->min, player->sec, player->msec) ); len = trap_SCR_StrlenForWidth( string, font, SCB_MEDIUMFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorYellow ); xoffset += SCB_MEDIUMFIELD_PIXELWIDTH; //print ping Q_snprintfz( string, sizeof(string), "%3i", player->ping ); pingcolor = SCR_SetPingColor( player->ping ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, *pingcolor ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print in_race Q_snprintfz( string, sizeof(string), "%s", player->ready==1 ? "IN RACE" : "" ); len = trap_SCR_StrlenForWidth( string, font, SCB_MEDIUMFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorGreen ); xoffset += SCB_MEDIUMFIELD_PIXELWIDTH; return trap_SCR_strHeight( font ); } int SCB_DrawDUELPlayerTab( scb_playertab_t *player, int x, int y, qboolean isright, struct mufont_s *font ) { vec4_t tabcolor; vec4_t *pingcolor; char string[MAX_STRING_CHARS]; int xname, alignname; int xscore; int scorewidth; int xstats; int len, xoffset = 0, yoffset = 0; int scoreCharSize = 48; struct mufont_s *tittleFont = cgs.fontSystemBig; if( !player || player->team < TEAM_RED || player->team > GS_MAX_TEAMS ) return 0; GS_TeamColor( player->team, tabcolor ); tabcolor[3] = SCB_BACKGROUND_ALPHA;//transparent Q_snprintfz( string, sizeof(string), "%i", player->score ); scorewidth = scoreCharSize * strlen(string); if( !isright ) //left side tab { xscore = x - (scorewidth + 8); // 8 is a space between tabs xname = x - (scorewidth + 16); alignname = ALIGN_RIGHT_BOTTOM; //draw box trap_R_DrawStretchPic( 0, y+scoreCharSize, x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, tabcolor, cgs.shaderWhite ); xoffset = -(16+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH); xstats = x + xoffset; } else { //right side tab xscore = x + 8; xname = x + scorewidth + 16; alignname = ALIGN_LEFT_BOTTOM; //draw box trap_R_DrawStretchPic( x, y+scoreCharSize, cg.refdef.width - x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, tabcolor, cgs.shaderWhite ); xoffset = 16; xstats = x + xoffset; } yoffset = 0; // print score box SCR_DrawBigNumbersString( xscore, y+yoffset, scoreCharSize, scoreCharSize, va("%i",player->score), colorWhite ); yoffset += scoreCharSize; // print name Q_snprintfz( string, sizeof(string), "%s%s", cgs.clientInfo[player->playernum].name, S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, tittleFont, SCB_TEAMNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( xname, y+yoffset, alignname, string, len, tittleFont, colorWhite ); trap_SCR_DrawString( xstats, y+yoffset, ALIGN_LEFT_TOP, "kill death suic ping", font, colorMdGrey ); yoffset+=trap_SCR_strHeight( font ); //print kills Q_snprintfz(string, sizeof(string), "%4i", player->kills); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y+yoffset, ALIGN_LEFT_TOP, string, len, font, colorYellow ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print deaths Q_snprintfz(string, sizeof(string), "%4i", player->deaths); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y+yoffset, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print suicides Q_snprintfz(string, sizeof(string), "%4i", player->suicides); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y+yoffset, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ping Q_snprintfz(string, sizeof(string), "%4i", player->ping); pingcolor = SCR_SetPingColor( player->ping ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y+yoffset, ALIGN_LEFT_TOP, string, len, font, *pingcolor ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ready in green if( cg.frame.match.state == MATCH_STATE_WARMUP && player->ready ) { if (!isright) { trap_SCR_DrawString( x, y, ALIGN_RIGHT_TOP, "R ", font, colorGreen ); } else { trap_SCR_DrawString( x + xoffset, y, ALIGN_LEFT_TOP, " R", font, colorGreen ); } } return yoffset; } #define SCB_TDM_PLAYERTAB_PIXELWIDTH (SCB_PLAYERNAME_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH) int SCB_DrawTDMPlayerTab( scb_playertab_t *player, int x, int y, qboolean isright, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; vec4_t *pingcolor; int eff; int len, xoffset = 0; //draw the box trap_R_DrawStretchPic( x + xoffset, y, SCB_TDM_PLAYERTAB_PIXELWIDTH, //width trap_SCR_strHeight( font ), //height 0, 0, 1, 1, SCB_ColorForPlayer(player), cgs.shaderWhite ); //print name Q_snprintfz( string, sizeof(string), "%s", cgs.clientInfo[player->playernum].name ); len = trap_SCR_StrlenForWidth( string, font, SCB_PLAYERNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_PLAYERNAME_PIXELWIDTH; //we align right, so offset before writting xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print score in yellow Q_snprintfz( string, sizeof(string), "%4i%s", player->score, S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_RIGHT_TOP, string, len, font, colorYellow ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print kills Q_snprintfz( string, sizeof(string), "%4i%s", player->kills, S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_RIGHT_TOP, string, len, font, colorWhite ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print deaths Q_snprintfz( string, sizeof(string), "%4i%s", player->deaths, S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_RIGHT_TOP, string, len, font, colorWhite ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print netto frags in orange eff = player->kills - (player->deaths + player->teamfrags); Q_snprintfz( string, sizeof(string), "%4i%s", eff , S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_RIGHT_TOP, string, len, font, (eff<0)?colorRed:colorOrange ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print pings Q_snprintfz( string, sizeof(string), "%4i%s", player->ping, S_COLOR_WHITE ); pingcolor = SCR_SetPingColor( player->ping ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_RIGHT_TOP, string, len, font, *pingcolor ); //xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ready in green if( cg.frame.match.state == MATCH_STATE_WARMUP && player->ready ) { if (!isright) { trap_SCR_DrawString( x, y, ALIGN_RIGHT_TOP, "R ", font, colorGreen ); } else { trap_SCR_DrawString( x + xoffset, y, ALIGN_LEFT_TOP, " R", font, colorGreen ); } } return trap_SCR_strHeight( font ); } #define SCB_CTF_PLAYERTAB_PIXELWIDTH (SCB_PLAYERNAME_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH+SCB_SMALLFIELD_PIXELWIDTH) int SCB_DrawCTFPlayerTab( scb_playertab_t *player, int x, int y, qboolean isright, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; vec4_t *pingcolor; int len, xoffset = 0; //draw the box trap_R_DrawStretchPic( x + xoffset, y, SCB_CTF_PLAYERTAB_PIXELWIDTH, //width trap_SCR_strHeight( font ), //height 0, 0, 1, 1, SCB_ColorForPlayer(player), cgs.shaderWhite ); //print name Q_snprintfz( string, sizeof(string), "%s", cgs.clientInfo[player->playernum].name ); len = trap_SCR_StrlenForWidth( string, font, SCB_PLAYERNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( x, y, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_PLAYERNAME_PIXELWIDTH; // print score in yellow Q_snprintfz( string, sizeof(string), "%4i", player->score ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, colorYellow ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; // print pings Q_snprintfz( string, sizeof(string), "%4i", player->ping ); pingcolor = SCR_SetPingColor( player->ping ); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y, ALIGN_LEFT_TOP, string, len, font, *pingcolor ); xoffset += SCB_SMALLFIELD_PIXELWIDTH; //print ready in green if( cg.frame.match.state == MATCH_STATE_WARMUP && player->ready ) { if (!isright) { trap_SCR_DrawString( x, y, ALIGN_RIGHT_TOP, "R ", font, colorGreen ); } else { trap_SCR_DrawString( x + xoffset, y, ALIGN_LEFT_TOP, " R", font, colorGreen ); } } return trap_SCR_strHeight( font ); } int SCB_DrawChallengerTab( scb_playertab_t *player, int x, int y, int align, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; if( !player || player->type != SCBTAB_CHALLENGER ) return 0; // wsw : pb : fix scoreboard color bug Q_snprintfz( string, sizeof(string), "%s%s%s %i%s", S_COLOR_WHITE, cgs.clientInfo[player->playernum].name, S_COLOR_WHITE, player->ping, S_COLOR_WHITE ); trap_SCR_DrawString( x, y, align, string, font, colorWhite ); return trap_SCR_strHeight( font ); } int SCB_DrawSpectatorTab( scb_playertab_t *player, int x, int y, int align, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; if( !player || player->type != SCBTAB_SPECTATOR ) return 0; Q_snprintfz( string, sizeof(string), "%s%s%s %s%i%s", S_COLOR_WHITE, cgs.clientInfo[player->playernum].name, S_COLOR_WHITE, SCR_SetPingColor(player->ping), player->ping, S_COLOR_WHITE ); trap_SCR_DrawString( x, y, align, string, font, colorWhite ); return trap_SCR_strHeight( font ); } int SCB_DrawConnectingPlayerTab( scb_playertab_t *player, int x, int y, int align, struct mufont_s *font ) { static char string[MAX_STRING_CHARS]; if( !player || player->type != SCBTAB_CONNECTING ) return 0; Q_snprintfz( string, sizeof(string), "%s%s %s%s%s", S_COLOR_WHITE, cgs.clientInfo[player->playernum].name, S_COLOR_GREY, "connecting", S_COLOR_WHITE ); trap_SCR_DrawString( x, y, align, string, font, colorWhite ); return trap_SCR_strHeight( font ); } void SCB_DrawSpectators( int x, int y ) { struct mufont_s *font = cgs.fontSystemSmall; int i, xoffset, yoffset; qboolean challengers = qfalse, spectators = qfalse; // Center the Waiting to play xoffset = 0; yoffset = 0; // check if there is anyone in the challengers list for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_CHALLENGER ) { yoffset += trap_SCR_strHeight( font ); challengers = qtrue; break; } } if( challengers ) { trap_SCR_DrawString( x + xoffset, y + yoffset, ALIGN_CENTER_TOP, "Challengers", font, colorMdGrey ); yoffset += trap_SCR_strHeight( font ); // draw challengers tabs for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_CHALLENGER ) { yoffset += SCB_DrawChallengerTab( &scb_players[i], x + xoffset, y + yoffset, ALIGN_CENTER_TOP, font ); } } } // check if there is anyone in the spectators list for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_SPECTATOR || scb_players[i].type == SCBTAB_CONNECTING ) { spectators = qtrue; yoffset += trap_SCR_strHeight( font ); break; } } if( spectators ) { trap_SCR_DrawString( x + xoffset, y + yoffset, ALIGN_CENTER_TOP, "Spectators", font, colorMdGrey ); yoffset += trap_SCR_strHeight( font ); // draw spectator tabs for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_CONNECTING ) { yoffset+=SCB_DrawConnectingPlayerTab( &scb_players[i], x + xoffset, y + yoffset, ALIGN_CENTER_TOP, font ); } else if( scb_players[i].type == SCBTAB_SPECTATOR ){ yoffset+=SCB_DrawSpectatorTab( &scb_players[i], x + xoffset, y + yoffset, ALIGN_CENTER_TOP, font ); } } } } int SCB_DrawPlayerStats( int x, int y ) { struct mufont_s *font = cgs.fontSystemSmall; int xoffset, yoffset, lines; int i, j, num_weapons, len, weap, xpos, width, done; gitem_t *it; char string[MAX_STRING_CHARS]; vec4_t color = { 0.5, 0.5, 0.5, 0.5f }; // dont display stats if( !cg_scoreboardStats->integer ) return 0; // total number of weapon num_weapons = WEAP_TOTAL-WEAP_GUNBLADE; width = (SCB_TINYFIELD_PIXELWIDTH + 2 * SCB_SMALLFIELD_PIXELWIDTH) * 2 + SCB_SMALLFIELD_PIXELWIDTH; xpos = -8 * SCB_TINYFIELD_PIXELWIDTH/2; // Center the box xoffset = xpos; yoffset = trap_SCR_strHeight( font ); // Room for header, it's actually written later if we have atleast one stat yoffset += trap_SCR_strHeight( font ); lines = 0; for( i = 0; i < num_weapons; ) { xoffset = xpos; // two weapons per line for( j = 0, done = 0; done < 2 && i + j < num_weapons; j++ ) { weap = WEAP_GUNBLADE + i + j; if( scb_player_stats[2*(i+j)] == -1 && scb_player_stats[2*(i+j)+1] == -1 ) continue; it = GS_FindItemByTag( weap ); // short name //trap_R_DrawStretchPic( x + xoffset, y + yoffset, trap_SCR_strHeight(font), trap_SCR_strHeight(font), // 0, 0, 1, 1, colorWhite, CG_MediaShader(cgs.media.shaderWeaponIcon[i+j]) ); Q_snprintfz( string, sizeof(string), "%s%2s", it->color, it->short_name); len = trap_SCR_StrlenForWidth( string, font, SCB_TINYFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, string, len, font, colorWhite ); xoffset += SCB_TINYFIELD_PIXELWIDTH; if( weap == WEAP_LASERGUN || weap == WEAP_ELECTROBOLT ) { // weak percent if( scb_player_stats[2*(i+j)] != -1 ) { Q_snprintfz( string, sizeof(string), "%2d%c", scb_player_stats[2*(i+j)], '%'); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, string, len, font, colorWhite ); } xoffset += SCB_SMALLFIELD_PIXELWIDTH; // strong percent if( scb_player_stats[2*(i+j)+1] != -1 ) { Q_snprintfz( string, sizeof(string), "%2d%c", scb_player_stats[2*(i+j)+1], '%'); len = trap_SCR_StrlenForWidth( string, font, SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, string, len, font, colorWhite ); } xoffset += SCB_SMALLFIELD_PIXELWIDTH; } else { Q_snprintfz( string, sizeof(string), "%2d%c", scb_player_stats[2*(i+j)+1], '%'); len = trap_SCR_StrlenForWidth( string, font, 2*SCB_SMALLFIELD_PIXELWIDTH ); trap_SCR_DrawStringLen( x + xoffset + SCB_SMALLFIELD_PIXELWIDTH, y + yoffset, ALIGN_CENTER_TOP, string, len, font, colorWhite ); xoffset += 2*SCB_SMALLFIELD_PIXELWIDTH; } // separator xoffset += SCB_SMALLFIELD_PIXELWIDTH; done++; } // next line if( done > 0 ) { lines++; yoffset += trap_SCR_strHeight( font ); } i += j; } if( lines ) { // if we drow anything, draw header and box too xoffset = xpos; yoffset = trap_SCR_strHeight( font ); // header len = trap_SCR_StrlenForWidth( "Weapon stats", font, width ); trap_SCR_DrawStringLen( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, "Weapon stats", len, font, colorMdGrey ); yoffset += trap_SCR_strHeight( font ); // box trap_R_DrawStretchPic( x + xoffset - SCB_TINYFIELD_PIXELWIDTH/2, y + yoffset, width + SCB_TINYFIELD_PIXELWIDTH, lines * trap_SCR_strHeight(font), 0, 0, 1, 1, color, cgs.shaderWhite ); return (trap_SCR_strHeight(font) * (2+lines)); } else { return 0; } } //=============================================================== // // DUEL SCOREBOARD // //=============================================================== //================ //SCR_DrawPlayerTab_DUEL //================ //================ //SCR_UpdateDUELScoreboard //================ void SCR_UpdateDUELScoreboard( char *s ) { char *token; //take out the layout id token = COM_Parse( &s ); if( !token ) return; scb_playercount = 0; while( s ) { token = COM_Parse( &s ); if( !Q_stricmp( token, "&g" ) ) { //read game name tab } else if( !Q_stricmp( token, "&p" ) ) { //read a player tab SCB_ParseDUELPlayerTab( &s ); } else if( !Q_stricmp( token, "&w" ) ) { // read a waiting list player tab SCB_ParseChallengerTab( &s ); } else if( !Q_stricmp( token, "&s" ) ) { // read a spectator tab SCB_ParseSpectatorTab( &s ); } else if( !Q_stricmp( token, "&c" ) ) { SCB_ParseConnectingPlayerTab( &s ); } else if( !Q_stricmp( token, "&z" ) ) { SCB_ParsePlayerStats( &s ); } } } //================ //SCR_DrawDUELScoreboard //================ void SCR_DrawDUELScoreboard( int x, int y, struct mufont_s *font ) { int i, xoffset, yoffset, ymaxoffset = 0; qboolean rightside = qfalse; //draw player tabs for( i = 0; i < scb_playercount; i++ ) { yoffset = 0; if( rightside ) { xoffset = 8; } else { xoffset = -8; } if( scb_players[i].type == SCBTAB_PLAYERDUEL ) yoffset += SCB_DrawDUELPlayerTab( &scb_players[i], x + xoffset, y + yoffset, rightside, font ); if( yoffset > ymaxoffset ) ymaxoffset = yoffset; rightside = !rightside; } xoffset = 0; yoffset = ymaxoffset + trap_SCR_strHeight(font); yoffset+=SCB_DrawPlayerStats( x + xoffset, y + yoffset ); SCB_DrawSpectators( x + xoffset, y + yoffset ); } //=============================================================== // // DM SCOREBOARD // //=============================================================== //================ //SCR_UpdateDMScoreboard //================ void SCR_UpdateDMScoreboard( char *s ) { char *token; //take out the layout id token = COM_Parse( &s ); if( !token ) return; scb_playercount = 0; while( s ) { token = COM_Parse( &s ); if( !Q_stricmp( token, "&g" ) ) //read game name tab { } else if( !Q_stricmp( token, "&p" ) ) { //read a player tab SCB_ParseFFAPlayerTab( &s ); } else if( !Q_stricmp( token, "&w" ) ) { // read a waiting list player tab SCB_ParseChallengerTab( &s ); } else if( !Q_stricmp( token, "&s" ) ) { //read a player tab SCB_ParseSpectatorTab( &s ); } else if( !Q_stricmp( token, "&c" ) ) { SCB_ParseConnectingPlayerTab( &s ); } else if( !Q_stricmp( token, "&z" ) ) { SCB_ParsePlayerStats( &s ); } } } //================ //SCR_DrawDMScoreboard //================ void SCR_DrawDMScoreboard( int x, int y, struct mufont_s *font ) { int i, xoffset, yoffset; yoffset = 0; xoffset = -SCB_FFA_PLAYERTAB_PIXELWIDTH/2; //draw players for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_PLAYERFFA ) yoffset += SCB_DrawFFAPlayerTab( &scb_players[i], x + xoffset, y + yoffset, font ); } xoffset = 0; yoffset += trap_SCR_strHeight(font); yoffset+=SCB_DrawPlayerStats( x + xoffset, y + yoffset ); SCB_DrawSpectators( x + xoffset, y + yoffset ); } //=============================================================== // // RACE SCOREBOARD // //=============================================================== //================ //SCR_UpdateDMScoreboard //================ void SCR_UpdateRACEScoreboard( char *s ) { char *token; //take out the layout id token = COM_Parse( &s ); if( !token ) return; scb_playercount = 0; while( s ) { token = COM_Parse( &s ); if( !Q_stricmp( token, "&g" ) ) { //read game name tab } else if( !Q_stricmp( token, "&p" ) ) { //read a player tab SCB_ParseRACEPlayerTab( &s ); } else if( !Q_stricmp( token, "&w" ) ) { // read a waiting list player tab SCB_ParseChallengerTab( &s ); } else if( !Q_stricmp( token, "&s" ) ) { //read a player tab SCB_ParseSpectatorTab( &s ); } else if( !Q_stricmp( token, "&c" ) ) { SCB_ParseConnectingPlayerTab( &s ); } } } //================ //SCR_DrawRACEScoreboard //================ void SCR_DrawRACEScoreboard( int x, int y, struct mufont_s *font ) { int i, xoffset = 0, yoffset; //yoffset at middle of the screen yoffset = 0; xoffset = -(SCB_FFA_PLAYERTAB_PIXELWIDTH/2); //draw players for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].type == SCBTAB_PLAYERRACE ) yoffset+=SCB_DrawRACEPlayerTab( &scb_players[i], x + xoffset, y + yoffset, font ); } yoffset += trap_SCR_strHeight(font); SCB_DrawSpectators( x, y + yoffset ); } //=============================================================== // // TDM SCOREBOARD // //=============================================================== typedef struct { int teamnum; int teamscore; qboolean updated; }cg_tdm_scoreboard_teams_t; static cg_tdm_scoreboard_teams_t tdmteams[GS_MAX_TEAMS]; int SCR_DrawTeamTab( int team, int x, int y, qboolean isright, struct mufont_s *font ) { vec4_t teamcolor; char string[MAX_STRING_CHARS]; int xname, alignname; int xscore; int scorewidth, scoreCharSize = 48; int xstats; int i, len; int yoffset=0; struct mufont_s *tittleFont = cgs.fontSystemBig; GS_TeamColor( team, teamcolor ); Q_snprintfz(string, sizeof(string), "%i", tdmteams[team].teamscore ); scorewidth = scoreCharSize * strlen(string); teamcolor[3] = SCB_BACKGROUND_ALPHA; // make transparent if( !isright ) //left side tab { xscore = x - (scorewidth + 8); // 8 is a space between tabs xname = x - (scorewidth + 16); alignname = ALIGN_RIGHT_BOTTOM; //draw box trap_R_DrawStretchPic( 0, y+scoreCharSize, x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); xstats = x - 16 - SCB_TDM_PLAYERTAB_PIXELWIDTH; } else { //right side tab xscore = x + 8; xname = x + scorewidth + 16; alignname = ALIGN_LEFT_BOTTOM; //draw box trap_R_DrawStretchPic( x, y+scoreCharSize, cg.refdef.width - x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); xstats = x + 16; } teamcolor[3] = 1.0f; // make solid yoffset = 0; // print score box SCR_DrawBigNumbersString( xscore, y+yoffset, scoreCharSize, scoreCharSize, va("%i",tdmteams[team].teamscore), colorWhite ); yoffset += scoreCharSize; // print name Q_snprintfz( string, sizeof(string), "%s%s", GS_TeamName(team), S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, tittleFont, SCB_TEAMNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( xname, y+yoffset, alignname, string, len, tittleFont, colorWhite ); // print players header { int xheaderoffset = 0; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_LEFT_TOP, "name", font, colorMdGrey ); xheaderoffset += SCB_PLAYERNAME_PIXELWIDTH; xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH;// we align right, so, offset before writting trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "pnts", font, colorMdGrey ); xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "kill", font, colorMdGrey ); xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "dead", font, colorMdGrey ); xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, " eff", font, colorMdGrey ); xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "ping", font, colorMdGrey ); //xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; yoffset += trap_SCR_strHeight( font ); } // print all players of this team for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].team == team ) { yoffset += SCB_DrawTDMPlayerTab( &scb_players[i], xstats, y+yoffset, isright, font ); } } return yoffset; } //================ //SCR_UpdateTDMScoreboard //================ void SCR_UpdateTDMScoreboard( char *s ) { char *token; int i, cteam = 0; //take out the layout id token = COM_Parse( &s ); if( !token ) return; for(i=0; i < GS_MAX_TEAMS; i++) tdmteams[i].updated = qfalse; scb_playercount = 0; while( s ) { token = COM_Parse( &s ); if( !Q_stricmp( token, "&t" ) ) //read a team tab { cteam = SCR_ParseValue( &s ); if( cteam < 0 || cteam > GS_MAX_TEAMS ) CG_Error("Invalid team value in CTF Scoreboard" ); tdmteams[cteam].teamnum = cteam; tdmteams[cteam].teamscore = SCR_ParseValue( &s ); tdmteams[cteam].updated = qtrue; } else if( !Q_stricmp( token, "&p" ) ) { //read a player tab SCB_ParseTDMPlayerTab( &s, cteam ); } else if( !Q_stricmp( token, "&w" ) ) { // read a waiting list player tab SCB_ParseChallengerTab( &s ); } else if( !Q_stricmp( token, "&s" ) ) { //read a spectator tab SCB_ParseSpectatorTab( &s ); } else if( !Q_stricmp( token, "&c" ) ) { SCB_ParseConnectingPlayerTab( &s ); } else if( !Q_stricmp( token, "&z" ) ) { SCB_ParsePlayerStats( &s ); } } } //================ //SCR_DrawTDMScoreboard //================ void SCR_DrawTDMScoreboard( int x, int y, struct mufont_s *font ) { int i, xoffset = 0, yoffset, ymaxoffset = 0; qboolean rightside = qfalse; //draw team tabs for( i = 0; i < GS_MAX_TEAMS; i++ ) { if( !tdmteams[i].updated ) { continue; } yoffset = 0; if( rightside ) { xoffset = 8; } else { xoffset = -8; } yoffset += SCR_DrawTeamTab( i, x + xoffset, y + yoffset, rightside, font ); rightside = !rightside; if( yoffset > ymaxoffset ) ymaxoffset = yoffset; xoffset = 0; } // so we draw the spectators below the teammembers // we do know how many players the biggest team has. yoffset = ymaxoffset + trap_SCR_strHeight(font); yoffset+=SCB_DrawPlayerStats( x + xoffset, y + yoffset ); SCB_DrawSpectators( x + xoffset, y + yoffset ); } //=============================================================== // // CTF SCOREBOARD // //=============================================================== typedef struct { int teamnum; int teamscore; int teamcaps; qboolean updated; }cg_ctf_scoreboard_teams_t; static cg_ctf_scoreboard_teams_t ctfteams[GS_MAX_TEAMS]; //================ // SCR_DrawTeamTabCTF //================ #define TAB_TEAM_MAX_NAME_LEN 16 int SCR_DrawTeamTabCTF( int team, int x, int y, qboolean isright, struct mufont_s *font ) { vec4_t teamcolor; char string[MAX_STRING_CHARS]; int len; int xname, alignname; int xscore; int scorewidth; int xstats; int scoreCharSize = 48; int i; int yoffset=0; struct mufont_s *tittleFont = cgs.fontSystemBig; GS_TeamColor( team, teamcolor ); Q_snprintfz(string, sizeof(string), "%i", ctfteams[team].teamscore ); scorewidth = scoreCharSize * strlen(string); teamcolor[3] = SCB_BACKGROUND_ALPHA; // make transparent if( !isright ) //left side tab { xscore = x - (scorewidth + 8); // 8 is a space between tabs xname = x - (scorewidth + 16); alignname = ALIGN_RIGHT_BOTTOM; //draw box trap_R_DrawStretchPic( 0, y+scoreCharSize, x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); xstats = x - 16 - SCB_CTF_PLAYERTAB_PIXELWIDTH; } else { //right side tab xscore = x + 8; xname = x + scorewidth + 16; alignname = ALIGN_LEFT_BOTTOM; //draw box trap_R_DrawStretchPic( x, y+scoreCharSize, cg.refdef.width - x, //width -((int)trap_SCR_strHeight( tittleFont )), //height 0, 0, 1, 1, teamcolor, cgs.shaderWhite ); xstats = x + 16; } teamcolor[3] = 1.0f; // make solid yoffset = 0; // print score box SCR_DrawBigNumbersString( xscore, y+yoffset, scoreCharSize, scoreCharSize, va("%i",ctfteams[team].teamcaps), colorWhite ); yoffset += scoreCharSize; // print name Q_snprintfz( string, sizeof(string), "%s%s", GS_TeamName(team), S_COLOR_WHITE ); len = trap_SCR_StrlenForWidth( string, tittleFont, SCB_TEAMNAME_PIXELWIDTH ); trap_SCR_DrawStringLen( xname, y+yoffset, alignname, string, len, tittleFont, colorWhite ); // print players header { int xheaderoffset = 0; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_LEFT_TOP, "name", font, colorMdGrey ); xheaderoffset += SCB_PLAYERNAME_PIXELWIDTH; xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH;// we align right, so, offset before writting trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "pnts", font, colorMdGrey ); xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; trap_SCR_DrawString( xstats+xheaderoffset, y+yoffset, ALIGN_RIGHT_TOP, "ping", font, colorMdGrey ); //xheaderoffset += SCB_SMALLFIELD_PIXELWIDTH; yoffset += trap_SCR_strHeight( font ); } // print all players of this team for( i = 0; i < scb_playercount; i++ ) { if( scb_players[i].team == team ) { yoffset+=SCB_DrawCTFPlayerTab( &scb_players[i], xstats, y+yoffset, isright, font ); } } return yoffset; } //================ //SCR_UpdateCTFScoreboard //================ void SCR_UpdateCTFScoreboard( char *s ) { char *token; int i, cteam = 0; //take out the layout id token = COM_Parse( &s ); if( !token ) return; for(i=0; i < GS_MAX_TEAMS; i++) ctfteams[i].updated = qfalse; scb_playercount = 0; while( s ) { token = COM_Parse( &s ); if( !Q_stricmp( token, "&t" ) ) //read a team tab { cteam = SCR_ParseValue( &s ); if( cteam < 0 || cteam > GS_MAX_TEAMS ) CG_Error("Invalid team value in CTF Scoreboard"); ctfteams[cteam].teamnum = cteam; ctfteams[cteam].teamscore = SCR_ParseValue( &s ); ctfteams[cteam].teamcaps = SCR_ParseValue( &s ); ctfteams[cteam].updated = qtrue; } else if( !Q_stricmp( token, "&p" ) ) { //read a player tab SCB_ParseCTFPlayerTab( &s, cteam ); } else if( !Q_stricmp( token, "&w" ) ) { // read a waiting list player tab SCB_ParseChallengerTab( &s ); } else if( !Q_stricmp( token, "&s" ) ) { //read a spectator tab SCB_ParseSpectatorTab( &s ); } else if( !Q_stricmp( token, "&c" ) ) { SCB_ParseConnectingPlayerTab( &s ); } else if( !Q_stricmp( token, "&z" ) ) { SCB_ParsePlayerStats( &s ); } } } //================ //SCR_DrawCTFScoreboard //================ void SCR_DrawCTFScoreboard( int x, int y, struct mufont_s *font ) { int t, xoffset, yoffset, ymaxoffset = 0; qboolean rightside = qfalse; //draw team tabs for( t = 0; t < GS_MAX_TEAMS; t++ ) { if( t!= TEAM_RED && t!= TEAM_BLUE ) // forces drawing the team tabs continue; yoffset = 0; if( rightside ) { xoffset = 8; } else { xoffset = -8; } yoffset += SCR_DrawTeamTabCTF( t, x + xoffset, y + yoffset, rightside, font ); if( yoffset > ymaxoffset ) ymaxoffset = yoffset; rightside = !rightside; xoffset = 0; } // so we draw the spectators below the teammembers // we do know how many players the biggest team has. yoffset = ymaxoffset + trap_SCR_strHeight(font); yoffset+=SCB_DrawPlayerStats( x, y + yoffset ); SCB_DrawSpectators( x, y + yoffset ); } //=============================================================== // // FIXED SCOREBOARD LAYOUT SCHEMES // //=============================================================== // Layouts were great when Q2 didn't have a cgame, and still are // great for the hud, but, since each mod has it's own cgame now, // we don't need to send the design through the netcode anymore. It's // enough for us now with sending the data, and use cgame for applying // that data to a fixed layout desing // Each layout can use it's own data format. To identify the string // as a fixed layout I decided to use a short token, beginning with // the 'and' character ( '&' ), and followed by a token of no more // than six characters. The rest of the string is interpreted at // it's own function, so the format it's particular to it. char scoreboard_name[16]; typedef struct { char *name; void (*DrawScoreboard)( int xpos, int ypos, struct mufont_s *font ); void (*UpdateScoreboard)( char *s ); }cg_scoreboard_templates_t; cg_scoreboard_templates_t cg_scoreboards[] = { {"&dms", SCR_DrawDMScoreboard, SCR_UpdateDMScoreboard}, {"&races", SCR_DrawRACEScoreboard, SCR_UpdateRACEScoreboard}, {"&duels", SCR_DrawDUELScoreboard, SCR_UpdateDUELScoreboard}, {"&tdms", SCR_DrawTDMScoreboard, SCR_UpdateTDMScoreboard}, {"&ctfs", SCR_DrawCTFScoreboard, SCR_UpdateCTFScoreboard}, {NULL} }; void SCR_DemoToggleScores_f( void ) { cg.demoShowScoreboard = !cg.demoShowScoreboard; } /* ================ SCR_DrawScoreboard ================ */ void SCR_DrawScoreboard( void ) { cg_scoreboard_templates_t *scboard; char title[20]; int xpos, ypos, len; struct mufont_s *font; //yoffset at middle of the screen xpos = (int)(cg.refdef.width * 0.5); ypos = (int)(cg.refdef.height * 0.25) - 24; font = trap_SCR_RegisterFont( cg_scoreboardFont->string ); if( !font ) { CG_Printf( "%sWarning: Invalid font in 'cg_scoreboardFont'. Reseting to default%s\n", S_COLOR_YELLOW, S_COLOR_WHITE ); trap_Cvar_Set( "cg_scoreboardFont", cg_scoreboardFont->dvalue ); font = trap_SCR_RegisterFont( cg_scoreboardFont->string ); } //title Q_snprintfz( title, sizeof(title), "WARSOW %s", GS_Gametype_ShortName( cg.frame.playerState.stats[STAT_GAMETYPE] ) ); Q_strupr( title ); trap_SCR_DrawString( xpos, ypos, ALIGN_CENTER_TOP, title, cgs.fontSystemBig, whiteTransparent ); ypos += trap_SCR_strHeight( cgs.fontSystemBig ); len = trap_SCR_StrlenForWidth( cgs.configStrings[CS_HOSTNAME], cgs.fontSystemSmall, cgs.vidWidth*0.75); trap_SCR_DrawStringLen( xpos, ypos, ALIGN_CENTER_TOP, cgs.configStrings[CS_HOSTNAME], len, cgs.fontSystemSmall, whiteTransparent ); ypos += trap_SCR_strHeight( cgs.fontSystemSmall ); if( scoreboard_name[0] != '&' ) { //must begin with return; } for( scboard = cg_scoreboards; scboard->name; scboard++ ) { if( !Q_stricmp( scboard->name, scoreboard_name ) ) { scboard->DrawScoreboard( xpos, ypos, font ); return; } } trap_SCR_DrawString( 16, 4*16, ALIGN_LEFT_TOP, "Invalid Scoreboard Template", cgs.fontSystemMedium, whiteTransparent ); if( developer->integer ) CG_Printf( "SCR_DrawScoreboard : Unrecognized scoreboard template\n"); } /* ================ SCR_UpdateScoreboardMessage ================ */ void SCR_UpdateScoreboardMessage( char *string ) { char *tok, *ptr; cg_scoreboard_templates_t *scboard; if( !string || !string[0] ) goto notfound; ptr = string; tok = COM_Parse( &ptr ); if( !tok || !tok[0] ) goto notfound; if( *tok != '&' ) //must begin with goto notfound; for( scboard = cg_scoreboards; scboard->name; scboard++ ) { if( !Q_stricmp( scboard->name, tok ) ) { Q_strncpyz( scoreboard_name, tok, sizeof(scoreboard_name) ); scboard->UpdateScoreboard( string ); return; } } notfound: // failed memset( scoreboard_name, 0, sizeof(scoreboard_name) ); if( developer->integer ) CG_Printf( "SCR_UpdateScoreboard : Unrecognized scoreboard template\n"); }