Example #1
0
/*
* SCR_DrawPlayerTab
*/
static int SCR_DrawPlayerTab( const char **ptrptr, int team, int x, int y, int panelWidth, struct qfontface_s *font, int pass )
{
	int dir, align, i, columncount;
	char type, string[MAX_STRING_CHARS];
	const char *token, *layout;
	int height, width, xoffset, yoffset;
	vec4_t teamcolor = { 0.0f, 0.0f, 0.0f, 1.0f }, color;
	int iconnum;
	struct shader_s *icon;
	bool highlight = false, trans = false;

	if( GS_TeamBasedGametype() )
	{
		dir = ( team == TEAM_ALPHA ) ? -1 : 1;
		align = ( team == TEAM_ALPHA ) ? ALIGN_RIGHT_TOP : ALIGN_LEFT_TOP;
	}
	else
	{
		dir = 0;
		align = ALIGN_CENTER_TOP;
	}

	xoffset = 0;
	yoffset = 0;

	height = trap_SCR_FontHeight( font );

	// start from the center again
	xoffset = CG_HorizontalAlignForWidth( 0, align, panelWidth );
	xoffset += ( SCB_CENTERMARGIN * dir );

	// draw the background
	columncount = 0;
	if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) )
		CG_TeamColor( team, teamcolor );

	// draw the player tab column titles
	layout = cgs.configStrings[CS_SCB_PLAYERTAB_LAYOUT];

	while( SCR_GetNextColumnLayout( &layout, NULL, &type, &width, font ) != NULL )
	{
		// grab the actual scoreboard data
		if( !SCR_ParseToken( ptrptr, &token ) )
			break;

		if( SCR_SkipColumn( type ) )
			continue;

		Vector4Copy( colorWhite, color ); // reset to white after each column
		icon = NULL;
		string[0] = 0;

		// interpret the data based on the type defined in the layout
		switch( type )
		{
		default:
			CG_Error( "SCR_DrawPlayerTab: Invalid player tab layout\n" );
			break;

		case 's': // is a string
			{
				char l10n[MAX_STRING_CHARS];
				Q_strncpyz( string, CG_TranslateColoredString( token, l10n, sizeof( l10n ) ), sizeof( string ) );
			}
			break;

		case 'n': // is a player name indicated by player number
			i = atoi( token );

			if( i < 0 ) // negative numbers toggle transparency on
			{
				trans = true;
				i = -1 - i;
			}

			if( i < 0 || i >= gs.maxclients )
				Q_strncpyz( string, "invalid", sizeof( string ) );
			else
				Q_strncpyz( string, cgs.clientInfo[i].name, sizeof( string ) );

			if( ISVIEWERENTITY( i + 1 ) ) // highlight if it's our own player
				highlight = true;

			break;
		case 'i': // is a integer (negatives are colored in red)
			i = atoi( token );
			Q_snprintfz( string, sizeof( string ), "%i", i );
			VectorCopy( i >= 0 ? colorWhite : colorRed, color );
			break;

		case 'f': // is a float
			Q_snprintfz( string, sizeof( string ), "%.2f", atof( token ) );
			break;

		case 'l': // p is an integer colored in latency style
			i = atoi( token );
			Q_snprintfz( string, sizeof( string ), "%i", i );
			CG_PingColor( i, color );
			break;

		case 'b': // is a Y/N boolean
			i = atoi( token );
			Q_snprintfz( string, sizeof( string ), "%s", CG_TranslateString( ( i != 0 ) ? "Yes" : "No" ) );
			VectorCopy( i ? colorGreen : colorRed, color );
			break;

		case 'p': // is a picture. It uses height for width to get a square
			iconnum = atoi( token );
			if( ( iconnum > 0 ) && ( iconnum < MAX_IMAGES ) )
				icon = cgs.imagePrecache[iconnum];
			break;

		case 't': // is a race time. Convert time into MM:SS:mm
			{
				unsigned int milli, min, sec;

				milli = (unsigned int)( atoi( token ) );
				if( !milli )
					Q_snprintfz( string, sizeof( string ), CG_TranslateString( "no time" ) );
				else
				{
					min = milli / 60000;
					milli -= min * 60000;
					sec = milli / 1000;
					milli -= sec * 1000;
					Q_snprintfz( string, sizeof( string ), va( "%02i:%02i.%03i", min, sec, milli ) );
				}
			}
			break;

		case 'r': // is a ready state tick that is hidden when not in warmup
			if( atoi( token ) )
				icon = CG_MediaShader( cgs.media.shaderVSayIcon[VSAY_YES] );
			break;
		}

		if( !width )
			continue;

		// draw the column background
		teamcolor[3] = SCB_BACKGROUND_ALPHA;
		if( columncount & 1 )
			teamcolor[3] -= 0.15;

		if( highlight )
			teamcolor[3] += 0.3;

		if( trans )
			color[3] = 0.3;

		if( !pass ) {
			trap_R_DrawStretchPic( x + xoffset, y + yoffset, width, height, 0, 0, 1, 1, teamcolor, cgs.shaderWhite );

			if( icon )
				SCR_AddPlayerIcon( icon, x + xoffset, y + yoffset, color[3], font );
		}

		// draw the column value
		if( pass && string[0] )
		{
			trap_SCR_DrawClampString( x + xoffset, y + yoffset, string,
				x + xoffset, y + yoffset,
				x + xoffset + width, y + yoffset + height, font, color );
		}

		columncount++;

		xoffset += width;
	}

	yoffset += height;
	return yoffset;
}
Example #2
0
/*
* SCR_DrawSpectators
*/
static int SCR_DrawSpectators( const char **ptrptr, int x, int y, int panelWidth, struct qfontface_s *font, bool havePing, const char *title, vec4_t titleColor, int pass )
{
	const char *token;
	char string[MAX_STRING_CHARS];
	int yoffset = 0, xoffset = 0;
	int playerNum, ping;
	int aligns[3], offsets[3];
	int colwidth, fullwidth, count = 0, height;
	bool titleDrawn = false;

	fullwidth = panelWidth * 1.5;
	if( fullwidth > cgs.vidWidth * 0.7 )
		fullwidth = cgs.vidWidth * 0.7;
	colwidth = fullwidth / 3;

	aligns[0] = ALIGN_CENTER_TOP;
	aligns[1] = ALIGN_LEFT_TOP;
	aligns[2] = ALIGN_RIGHT_TOP;

	offsets[0] = 0;
	offsets[1] = -fullwidth * 0.5;
	offsets[2] = fullwidth * 0.5;

	assert( ptrptr && *ptrptr );

	height = trap_SCR_FontHeight( font );
	yoffset = height;

	// draw spectators
	while( *ptrptr )
	{
		if( !SCR_ParseToken( ptrptr, &token ) )
			break;

		// first token is played id
		playerNum = atoi( token );
		if( playerNum < 0 || playerNum >= gs.maxclients )
			break;

		if( havePing )
		{
			// get a second token
			if( !SCR_ParseToken( ptrptr, &token ) )
				break;

			// second token is ping
			ping = atoi( token );

			// draw the spectator
			if( ping < 0 )
				Q_snprintfz( string, sizeof( string ), "%s%s ...", cgs.clientInfo[playerNum].name, S_COLOR_WHITE );
			else
				Q_snprintfz( string, sizeof( string ), "%s%s %i", cgs.clientInfo[playerNum].name, S_COLOR_WHITE, ping );
		}
		else
		{
			Q_snprintfz( string, sizeof( string ), "%s%s", cgs.clientInfo[playerNum].name, S_COLOR_WHITE );
		}

		// draw title if there are any spectators
		if( !titleDrawn )
		{
			titleDrawn = true;
			if( pass ) {
				trap_SCR_DrawString( x, y + yoffset, ALIGN_CENTER_TOP,
					CG_TranslateString( title ), font, titleColor );
			}
			yoffset += height;
		}

		xoffset = offsets[count] + CG_HorizontalAlignForWidth( 0, aligns[count], trap_SCR_strWidth( string, font, 0 ) );

		if ( pass ) {
			// fixme: the boxes aren't actually correctly aligned
			trap_SCR_DrawClampString( x + xoffset, y + yoffset, string, x + xoffset, y + yoffset, x + xoffset + colwidth, y + yoffset + height, font, colorWhite );
		}

		count++;
		if( count > 2 )
		{
			count = 0;
			yoffset += height;
		}
	}

	if( count )
		yoffset += height;
	return yoffset;
}
Example #3
0
/*
* SCR_DrawTeamTab
*/
static int SCR_DrawTeamTab( const char **ptrptr, int *curteam, int x, int y, int panelWidth, struct qfontface_s *font, struct qfontface_s *titleFont, int pass )
{
	const char *token;
	const char *layout, *titles;
	char type;
	int team, team_score, team_ping;
	int yoffset = 0, xoffset = 0;
	int dir = 0, align, width, height;
	vec4_t teamcolor = { 0.0f, 0.0f, 0.0f, 1.0f }, pingcolor;

	// team tab is always the same. Sets the current team and draws its score

	if( !(*ptrptr) || !(*ptrptr[0]) || *ptrptr[0] == '&' )
		return yoffset;

	team = CG_ParseValue( ptrptr );
	if( team < TEAM_PLAYERS || team > TEAM_BETA )
		CG_Error( "SCR_ParseTeamTab: Invalid team value\n" );

	*curteam = team;

	if( *ptrptr[0] == '&' )
		return yoffset;

	team_score = CG_ParseValue( ptrptr );

	if( *ptrptr[0] == '&' )
		return yoffset;

	team_ping = CG_ParseValue( ptrptr );

	if( ( team == TEAM_ALPHA ) || ( team == TEAM_BETA ) )
		CG_TeamColor( team, teamcolor );
	teamcolor[3] = SCB_BACKGROUND_ALPHA; // make transparent

	if( GS_TeamBasedGametype() ) // we only draw the team tabs in team based gametypes
	{
		dir = ( team == TEAM_ALPHA ) ? -1 : 1;
		align = ( team == TEAM_ALPHA ) ? ALIGN_RIGHT_TOP : ALIGN_LEFT_TOP;

		// draw the tab

		xoffset = ( SCB_CENTERMARGIN * dir );

		width = ( cgs.vidWidth * 0.5 ) - SCB_CENTERMARGIN;
		height = trap_SCR_FontHeight( titleFont ) + 2;

		if( !pass ) {
			CG_DrawAlignPic( x + xoffset, y + yoffset + SCB_SCORENUMBER_SIZE - height,
				width, height, align, teamcolor, cgs.shaderWhite );
		}

		if( pass ) {
			xoffset += ( ( 16 * cgs.vidHeight / 600 ) * dir );

			CG_DrawHUDNumeric( x + xoffset, y + yoffset, align, colorWhite,
				SCB_SCORENUMBER_SIZE, SCB_SCORENUMBER_SIZE, team_score );

			xoffset += ( ( SCB_SCORENUMBER_SIZE * strlen(va("%i", team_score)) + ( 16 * cgs.vidHeight / 600 ) ) * dir );
			trap_SCR_DrawStringWidth( x + xoffset + ( ( SCB_TINYFIELD_PIXELWIDTH + ( 16 * cgs.vidHeight / 600 ) ) * dir ),
				y + yoffset + SCB_SCORENUMBER_SIZE - (trap_SCR_FontHeight( titleFont ) + 1),
				align, GS_TeamName( team ), SCB_TEAMNAME_PIXELWIDTH, titleFont, colorWhite );

			CG_PingColor( team_ping, pingcolor );
			trap_SCR_DrawStringWidth( x + xoffset,
				y + yoffset + SCB_SCORENUMBER_SIZE - (trap_SCR_FontHeight( font ) + 1),
				align, va( "%i", team_ping ), SCB_TINYFIELD_PIXELWIDTH, font, pingcolor );
		}

		yoffset += SCB_SCORENUMBER_SIZE;
	}
	else
	{
		dir = 0;
		align = ALIGN_CENTER_TOP;
	}

	// draw the player tab column titles
	layout = cgs.configStrings[CS_SCB_PLAYERTAB_LAYOUT];
	titles = cgs.configStrings[CS_SCB_PLAYERTAB_TITLES];

	height = trap_SCR_FontHeight( font );

	// start from the center again
	xoffset = CG_HorizontalAlignForWidth( 0, align, panelWidth );
	xoffset += ( SCB_CENTERMARGIN * dir );

	while( ( token = SCR_GetNextColumnLayout( &layout, &titles, &type, &width, font ) ) != NULL )
	{
		if( SCR_SkipColumn( type ) )
			continue;

		if( width )
		{
			if( pass ) {
				trap_SCR_DrawClampString( x + xoffset, y + yoffset, CG_TranslateString( token ),
					x + xoffset, y + yoffset, x + xoffset + width, y + yoffset + height, font, colorWhite );
			}
			xoffset += width;
		}
	}

	yoffset += trap_SCR_FontHeight( font );

	return yoffset;
}
Example #4
0
/*
** CG_DrawChat
*/
void CG_DrawChat( cg_gamechat_t *chat, int x, int y, char *fontName, struct qfontface_s *font, int fontSize,
				  int width, int height, int padding_x, int padding_y, vec4_t backColor, struct shader_s *backShader ) {
	int i, j;
	int s, e, w;
	int utf_len;
	int l, total_lines, lines;
	int x_offset, y_offset;
	int font_height;
	int pass;
	int lastcolor;
	int message_mode;
	int wait_time, fade_time;
	const cg_gamemessage_t *msg;
	const char *text;
	char tstr[GAMECHAT_STRING_SIZE];
	vec4_t fontColor;
	bool chat_active = false;
	bool background_drawn = false;
	int corner_radius = 12 * cgs.vidHeight / 600;
	int background_y;
	int first_candidate;

	font_height = trap_SCR_FontHeight( font );
	message_mode = (int)trap_Cvar_Value( "con_messageMode" );
	chat_active = ( chat->lastMsgTime + GAMECHAT_WAIT_IN_TIME + GAMECHAT_FADE_IN_TIME > cg.realTime || message_mode );
	lines = 0;
	total_lines = /*!message_mode ? 0 : */ 1;

	if( chat_active ) {
		wait_time = GAMECHAT_WAIT_IN_TIME;
		fade_time = GAMECHAT_FADE_IN_TIME;
	} else {
		wait_time = GAMECHAT_WAIT_OUT_TIME;
		fade_time = GAMECHAT_FADE_OUT_TIME;
	}

	if( chat_active != chat->lastActive ) {
		// smooth fade ins and fade outs
		chat->lastActiveChangeTime = cg.realTime - ( 1.0 - chat->activeFrac ) * ( wait_time + fade_time );
	}

	if( cg.realTime >= chat->lastActiveChangeTime + wait_time ) {
		int time_diff, time_interval;

		time_diff = cg.realTime - ( chat->lastActiveChangeTime + wait_time );
		time_interval = fade_time;

		if( time_diff <= time_interval ) {
			chat->activeFrac = (float)time_diff / time_interval;
		} else {
			chat->activeFrac = 1;
		}
	} else {
		chat->activeFrac = 0;
	}

	if( chat_active ) {
		backColor[3] *= chat->activeFrac;
	} else {
		backColor[3] *= ( 1.0 - chat->activeFrac );
	}

	for( i = 0; i < GAMECHAT_STACK_SIZE; i++ ) {
		bool old_msg;

		l = chat->nextMsg - 1 - i;
		if( l < 0 ) {
			l = GAMECHAT_STACK_SIZE + l;
		}

		msg = &chat->messages[l];
		text = msg->text;
		old_msg = !message_mode && ( cg.realTime > msg->time + GAMECHAT_NOTIFY_TIME );

		if( !background_drawn && backColor[3] ) {
			if( old_msg ) {
				// keep the box being drawn for a while to prevent it from flickering
				// upon arrival of the possibly entered chat message
				if( !( !chat_active && cg.realTime <= chat->lastActiveChangeTime + 200 ) ) {
					break;
				}
			}

			background_y = y;
			trap_R_DrawStretchPic( x, background_y, width, height - corner_radius,
								   0.0f, 0.0f, 1.0f, 0.5f, backColor, backShader );
			background_y += height - corner_radius;

			if( trap_IN_IME_GetCandidates( NULL, 0, 10, NULL, &first_candidate ) ) {
				int candidates_height = ( first_candidate ? 3 : 5 ) * font_height;
				trap_R_DrawStretchPic( x, background_y, width, candidates_height,
									   0.0f, 0.5f, 1.0f, 0.5f, backColor, backShader );
				background_y += candidates_height;
			}

			trap_R_DrawStretchPic( x, background_y, corner_radius, corner_radius,
								   0.0f, 0.5f, 0.5f, 1.0f, backColor, backShader );
			trap_R_DrawStretchPic( x + corner_radius, background_y, width - corner_radius * 2, corner_radius,
								   0.5f, 0.5f, 0.5f, 1.0f, backColor, backShader );
			trap_R_DrawStretchPic( x + width - corner_radius, background_y, corner_radius, corner_radius,
								   0.5f, 0.5f, 1.0f, 1.0f, backColor, backShader );

			background_drawn = true;
		}

		// unless user is typing something, only display recent messages
		if( old_msg ) {
			break;
		}

		pass = 0;
		lines = 0;
		lastcolor = ColorIndex( COLOR_WHITE );

parse_string:
		l = 1;
		s = e = 0;
		while( 1 ) {
			int len;

			memset( tstr, 0, sizeof( tstr ) );

			// skip whitespaces at start
			for( ; text[s] == '\n' || Q_IsBreakingSpace( text + s ); s = Q_Utf8SyncPos( text, s + 1, UTF8SYNC_RIGHT ) ) ;

			// empty string
			if( !text[s] ) {
				break;
			}

			w = -1;
			len = trap_SCR_StrlenForWidth( text + s, font, width - padding_x * 2 );
			clamp_low( len, 1 );

			for( j = s; ( j < ( s + len ) ) && text[j] != '\0'; j += utf_len ) {
				utf_len = Q_Utf8SyncPos( text + j, 1, UTF8SYNC_RIGHT );
				memcpy( tstr + j - s, text + j, utf_len );

				if( text[j] == '\n' || Q_IsBreakingSpace( text + j ) ) {
					w = j; // last whitespace
				}
				if( text[j] == '\n' ) {
					break;
				}
			}
			e = j; // end

			// try to word avoid splitting words, unless no other options
			if( text[j] != '\0' && w > 0 ) {
				// stop at the last encountered whitespace
				j = w;
			}

			tstr[j - s] = '\0';

			Vector4Copy( color_table[lastcolor], fontColor );
			fontColor[3] = chat_active ? chat->activeFrac : 1.0 - chat->activeFrac;

			if( pass ) {
				// now actually render the line
				x_offset = padding_x;
				y_offset = height - padding_y - font_height - ( total_lines + lines - l ) * ( font_height + 2 );
				if( y_offset < padding_y ) {
					break;
				}

				trap_SCR_DrawClampString( x + x_offset, y + y_offset, tstr,
										  x + padding_x, y + padding_y, x - padding_x + width, y - padding_y + height, font, fontColor );

				l++;
			} else {
				// increase the lines counter
				lines++;
			}

			if( !text[j] ) {
				// fast path: we don't need two passes in case of one-liners..
				if( lines == 1 ) {
					x_offset = padding_x;
					y_offset = height - font_height - total_lines * ( font_height + 2 );
					if( y_offset < padding_y ) {
						break;
					}

					trap_SCR_DrawClampString( x + x_offset, y + y_offset, tstr,
											  x + padding_x, y + padding_y, x - padding_x + width, y - padding_y + height, font, fontColor );

					total_lines++;
					pass++;
				}
				break;
			}

			if( pass ) {
				// grab the last color token to carry it over to the next line
				lastcolor = Q_ColorStrLastColor( lastcolor, tstr, j - s );
			}

			s = j;
		}

		if( !pass ) {
			pass++;
			goto parse_string;
		} else {
			total_lines += lines;
		}
	}

	// let the engine know where the input line should be drawn
	trap_SCR_DrawChat( x + padding_x, y + height - padding_y - font_height, width - padding_x, font );

	chat->lastActive = chat_active;
}