1 /*
2 ===========================================================================
3 
4 Return to Castle Wolfenstein multiplayer GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”).
8 
9 RTCW MP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 RTCW MP Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with RTCW MP Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
30 
31 #include "client.h"
32 
33 qboolean scr_initialized;           // ready to draw
34 
35 cvar_t      *cl_timegraph;
36 cvar_t      *cl_debuggraph;
37 cvar_t      *cl_graphheight;
38 cvar_t      *cl_graphscale;
39 cvar_t      *cl_graphshift;
40 
41 /*
42 ================
43 SCR_DrawNamedPic
44 
45 Coordinates are 640*480 virtual values
46 =================
47 */
SCR_DrawNamedPic(float x,float y,float width,float height,const char * picname)48 void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ) {
49 	qhandle_t hShader;
50 
51 	assert( width != 0 );
52 
53 	hShader = re.RegisterShader( picname );
54 	SCR_AdjustFrom640( &x, &y, &width, &height );
55 	re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
56 }
57 
58 
59 /*
60 ================
61 SCR_AdjustFrom640
62 
63 Adjusted for resolution and screen aspect ratio
64 ================
65 */
SCR_AdjustFrom640(float * x,float * y,float * w,float * h)66 void SCR_AdjustFrom640( float *x, float *y, float *w, float *h ) {
67 	float xscale;
68 	float yscale;
69 
70 #if 0
71 	// adjust for wide screens
72 	if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
73 		*x += 0.5 * ( cls.glconfig.vidWidth - ( cls.glconfig.vidHeight * 640 / 480 ) );
74 	}
75 #endif
76 
77 	// scale for screen sizes
78 	xscale = cls.glconfig.vidWidth / 640.0;
79 	yscale = cls.glconfig.vidHeight / 480.0;
80 	if ( x ) {
81 		*x *= xscale;
82 	}
83 	if ( y ) {
84 		*y *= yscale;
85 	}
86 	if ( w ) {
87 		*w *= xscale;
88 	}
89 	if ( h ) {
90 		*h *= yscale;
91 	}
92 }
93 
94 /*
95 ================
96 SCR_FillRect
97 
98 Coordinates are 640*480 virtual values
99 =================
100 */
SCR_FillRect(float x,float y,float width,float height,const float * color)101 void SCR_FillRect( float x, float y, float width, float height, const float *color ) {
102 	re.SetColor( color );
103 
104 	SCR_AdjustFrom640( &x, &y, &width, &height );
105 	re.DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cls.whiteShader );
106 
107 	re.SetColor( NULL );
108 }
109 
110 
111 /*
112 ================
113 SCR_DrawPic
114 
115 Coordinates are 640*480 virtual values
116 =================
117 */
SCR_DrawPic(float x,float y,float width,float height,qhandle_t hShader)118 void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
119 	SCR_AdjustFrom640( &x, &y, &width, &height );
120 	re.DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
121 }
122 
123 
124 
125 /*
126 ** SCR_DrawChar
127 ** chars are drawn at 640*480 virtual screen size
128 */
SCR_DrawChar(int x,int y,float size,int ch)129 static void SCR_DrawChar( int x, int y, float size, int ch ) {
130 	int row, col;
131 	float frow, fcol;
132 	float ax, ay, aw, ah;
133 
134 	ch &= 255;
135 
136 	if ( ch == ' ' ) {
137 		return;
138 	}
139 
140 	if ( y < -size ) {
141 		return;
142 	}
143 
144 	ax = x;
145 	ay = y;
146 	aw = size;
147 	ah = size;
148 	SCR_AdjustFrom640( &ax, &ay, &aw, &ah );
149 
150 	row = ch >> 4;
151 	col = ch & 15;
152 
153 	frow = row * 0.0625;
154 	fcol = col * 0.0625;
155 	size = 0.0625;
156 
157 	re.DrawStretchPic( ax, ay, aw, ah,
158 					   fcol, frow,
159 					   fcol + size, frow + size,
160 					   cls.charSetShader );
161 }
162 
163 /*
164 ** SCR_DrawSmallChar
165 ** small chars are drawn at native screen resolution
166 */
SCR_DrawSmallChar(int x,int y,int ch)167 void SCR_DrawSmallChar( int x, int y, int ch ) {
168 	int row, col;
169 	float frow, fcol;
170 	float size;
171 
172 	ch &= 255;
173 
174 	if ( ch == ' ' ) {
175 		return;
176 	}
177 
178 	if ( y < -SMALLCHAR_HEIGHT ) {
179 		return;
180 	}
181 
182 	row = ch >> 4;
183 	col = ch & 15;
184 
185 	frow = row * 0.0625;
186 	fcol = col * 0.0625;
187 	size = 0.0625;
188 
189 	re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT,
190 					   fcol, frow,
191 					   fcol + size, frow + size,
192 					   cls.charSetShader );
193 }
194 
195 
196 /*
197 ==================
198 SCR_DrawBigString[Color]
199 
200 Draws a multi-colored string with a drop shadow, optionally forcing
201 to a fixed color.
202 
203 Coordinates are at 640 by 480 virtual resolution
204 ==================
205 */
SCR_DrawStringExt(int x,int y,float size,const char * string,float * setColor,qboolean forceColor,qboolean noColorEscape)206 void SCR_DrawStringExt( int x, int y, float size, const char *string, float *setColor, qboolean forceColor,
207 		qboolean noColorEscape ) {
208 	vec4_t color;
209 	const char  *s;
210 	int xx;
211 
212 	// draw the drop shadow
213 	color[0] = color[1] = color[2] = 0;
214 	color[3] = setColor[3];
215 	re.SetColor( color );
216 	s = string;
217 	xx = x;
218 	while ( *s ) {
219 		if ( !noColorEscape && Q_IsColorString( s ) ) {
220 			s += 2;
221 			continue;
222 		}
223 		SCR_DrawChar( xx + 2, y + 2, size, *s );
224 		xx += size;
225 		s++;
226 	}
227 
228 
229 	// draw the colored text
230 	s = string;
231 	xx = x;
232 	re.SetColor( setColor );
233 	while ( *s ) {
234 		if ( Q_IsColorString( s ) ) {
235 			if ( !forceColor ) {
236 				memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) );
237 				color[3] = setColor[3];
238 				re.SetColor( color );
239 			}
240 			if ( !noColorEscape ) {
241 				s += 2;
242 				continue;
243 			}
244 		}
245 		SCR_DrawChar( xx, y, size, *s );
246 		xx += size;
247 		s++;
248 	}
249 	re.SetColor( NULL );
250 }
251 
252 
SCR_DrawBigString(int x,int y,const char * s,float alpha,qboolean noColorEscape)253 void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ) {
254 	float color[4];
255 
256 	color[0] = color[1] = color[2] = 1.0;
257 	color[3] = alpha;
258 	SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qfalse, noColorEscape );
259 }
260 
SCR_DrawBigStringColor(int x,int y,const char * s,vec4_t color,qboolean noColorEscape)261 void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ) {
262 	SCR_DrawStringExt( x, y, BIGCHAR_WIDTH, s, color, qtrue, noColorEscape );
263 }
264 
265 
266 /*
267 ==================
268 SCR_DrawSmallString[Color]
269 
270 Draws a multi-colored string with a drop shadow, optionally forcing
271 to a fixed color.
272 ==================
273 */
SCR_DrawSmallStringExt(int x,int y,const char * string,float * setColor,qboolean forceColor,qboolean noColorEscape)274 void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor,
275 		qboolean noColorEscape ) {
276 	vec4_t color;
277 	const char  *s;
278 	int xx;
279 
280 	// draw the colored text
281 	s = string;
282 	xx = x;
283 	re.SetColor( setColor );
284 	while ( *s ) {
285 		if ( Q_IsColorString( s ) ) {
286 			if ( !forceColor ) {
287 				memcpy( color, g_color_table[ColorIndex( *( s + 1 ) )], sizeof( color ) );
288 				color[3] = setColor[3];
289 				re.SetColor( color );
290 			}
291 			if ( !noColorEscape ) {
292 				s += 2;
293 				continue;
294 			}
295 		}
296 		SCR_DrawSmallChar( xx, y, *s );
297 		xx += SMALLCHAR_WIDTH;
298 		s++;
299 	}
300 	re.SetColor( NULL );
301 }
302 
303 
304 
305 /*
306 ** SCR_Strlen -- skips color escape codes
307 */
SCR_Strlen(const char * str)308 static int SCR_Strlen( const char *str ) {
309 	const char *s = str;
310 	int count = 0;
311 
312 	while ( *s ) {
313 		if ( Q_IsColorString( s ) ) {
314 			s += 2;
315 		} else {
316 			count++;
317 			s++;
318 		}
319 	}
320 
321 	return count;
322 }
323 
324 /*
325 ** SCR_GetBigStringWidth
326 */
SCR_GetBigStringWidth(const char * str)327 int SCR_GetBigStringWidth( const char *str ) {
328 	return SCR_Strlen( str ) * BIGCHAR_WIDTH;
329 }
330 
331 
332 //===============================================================================
333 
334 /*
335 =================
336 SCR_DrawDemoRecording
337 =================
338 */
SCR_DrawDemoRecording(void)339 void SCR_DrawDemoRecording( void ) {
340 	char string[1024];
341 	int pos;
342 
343 	if ( !clc.demorecording ) {
344 		return;
345 	}
346 
347 	pos = FS_FTell( clc.demofile );
348 	sprintf( string, "RECORDING %s: %ik", clc.demoName, pos / 1024 );
349 
350 	SCR_DrawStringExt( 320 - strlen( string ) * 4, 20, 8, string, g_color_table[7], qtrue, qfalse );
351 }
352 
353 #ifdef USE_VOIP
354 /*
355 =================
356 SCR_DrawVoipMeter
357 =================
358 */
SCR_DrawVoipMeter(void)359 void SCR_DrawVoipMeter( void ) {
360 	char	buffer[16];
361 	char	string[256];
362 	int limit, i;
363 
364 	if (!cl_voipShowMeter->integer)
365 		return;  // player doesn't want to show meter at all.
366 	else if (!cl_voipSend->integer)
367 		return;  // not recording at the moment.
368 	else if (clc.state != CA_ACTIVE)
369 		return;  // not connected to a server.
370 	else if (!clc.voipEnabled)
371 		return;  // server doesn't support VoIP.
372 	else if (clc.demoplaying)
373 		return;  // playing back a demo.
374 	else if (!cl_voip->integer)
375 		return;  // client has VoIP support disabled.
376 
377 	limit = (int) (clc.voipPower * 10.0f);
378 	if (limit > 10)
379 		limit = 10;
380 
381 	for (i = 0; i < limit; i++)
382 		buffer[i] = '*';
383 	while (i < 10)
384 		buffer[i++] = ' ';
385 	buffer[i] = '\0';
386 
387 	sprintf( string, "VoIP: [%s]", buffer );
388 	SCR_DrawStringExt( 320 - strlen( string ) * 4, 10, 8, string, g_color_table[7], qtrue, qfalse );
389 }
390 #endif
391 
392 /*
393 =================
394 SCR_ShowPing
395 =================
396 */
SCR_ShowPing(void)397 void SCR_ShowPing( void ) {
398 	char	string[11];
399 	int	ping, w, x;
400 
401 	if (!cl_showPing->integer)
402 		return;
403 
404 	if ( Cvar_VariableIntegerValue( "ui_limboMode" ) )
405 		return;
406 
407 	ping = cl.snap.ping;
408 
409 	Com_sprintf( string, sizeof( string ), "ping: %i", ping );
410 
411 	w = strlen( string ) * TINYCHAR_WIDTH;
412 	x = 320 - ( w * 0.5 );
413 
414 	if ( ping < 999 )
415 		SCR_DrawStringExt( x, 386, TINYCHAR_HEIGHT, string, g_color_table[7], qtrue, qfalse );
416 	else
417 		return;
418 }
419 
420 /*
421 ===============================================================================
422 
423 DEBUG GRAPH
424 
425 ===============================================================================
426 */
427 
428 static int current;
429 static float values[1024];
430 
431 /*
432 ==============
433 SCR_DebugGraph
434 ==============
435 */
SCR_DebugGraph(float value)436 void SCR_DebugGraph( float value ) {
437 	values[current] = value;
438 	current = (current + 1) % ARRAY_LEN(values);
439 }
440 
441 /*
442 ==============
443 SCR_DrawDebugGraph
444 ==============
445 */
SCR_DrawDebugGraph(void)446 void SCR_DrawDebugGraph( void ) {
447 	int a, x, y, w, i, h;
448 	float v;
449 
450 	//
451 	// draw the graph
452 	//
453 	w = cls.glconfig.vidWidth;
454 	x = 0;
455 	y = cls.glconfig.vidHeight;
456 	re.SetColor( g_color_table[0] );
457 	re.DrawStretchPic( x, y - cl_graphheight->integer,
458 					   w, cl_graphheight->integer, 0, 0, 0, 0, cls.whiteShader );
459 	re.SetColor( NULL );
460 
461 	for ( a = 0 ; a < w ; a++ )
462 	{
463 		i = (ARRAY_LEN(values)+current-1-(a % ARRAY_LEN(values))) % ARRAY_LEN(values);
464 		v = values[i];
465 		v = v * cl_graphscale->integer + cl_graphshift->integer;
466 
467 		if ( v < 0 ) {
468 			v += cl_graphheight->integer * ( 1 + (int)( -v / cl_graphheight->integer ) );
469 		}
470 		h = (int)v % cl_graphheight->integer;
471 		re.DrawStretchPic( x + w - 1 - a, y - h, 1, h, 0, 0, 0, 0, cls.whiteShader );
472 	}
473 }
474 
475 //=============================================================================
476 
477 /*
478 ==================
479 SCR_Init
480 ==================
481 */
SCR_Init(void)482 void SCR_Init( void ) {
483 	cl_timegraph = Cvar_Get( "timegraph", "0", CVAR_CHEAT );
484 	cl_debuggraph = Cvar_Get( "debuggraph", "0", CVAR_CHEAT );
485 	cl_graphheight = Cvar_Get( "graphheight", "32", CVAR_CHEAT );
486 	cl_graphscale = Cvar_Get( "graphscale", "1", CVAR_CHEAT );
487 	cl_graphshift = Cvar_Get( "graphshift", "0", CVAR_CHEAT );
488 
489 	scr_initialized = qtrue;
490 }
491 
492 
493 //=======================================================
494 
495 /*
496 ==================
497 SCR_DrawScreenField
498 
499 This will be called twice if rendering in stereo mode
500 ==================
501 */
SCR_DrawScreenField(stereoFrame_t stereoFrame)502 void SCR_DrawScreenField( stereoFrame_t stereoFrame ) {
503 	qboolean uiFullscreen;
504 
505 	re.BeginFrame( stereoFrame );
506 
507 	uiFullscreen = (uivm && VM_Call( uivm, UI_IS_FULLSCREEN ));
508 
509 	// wide aspect ratio screens need to have the sides cleared
510 	// unless they are displaying game renderings
511 	if ( uiFullscreen || clc.state < CA_LOADING ) {
512 		if ( cls.glconfig.vidWidth * 480 > cls.glconfig.vidHeight * 640 ) {
513 			re.SetColor( g_color_table[0] );
514 			re.DrawStretchPic( 0, 0, cls.glconfig.vidWidth, cls.glconfig.vidHeight, 0, 0, 0, 0, cls.whiteShader );
515 			re.SetColor( NULL );
516 		}
517 	}
518 
519 	// if the menu is going to cover the entire screen, we
520 	// don't need to render anything under it
521 	if ( uivm && !uiFullscreen ) {
522 		switch( clc.state ) {
523 		default:
524 			Com_Error( ERR_FATAL, "SCR_DrawScreenField: bad clc.state" );
525 			break;
526 		case CA_CINEMATIC:
527 			SCR_DrawCinematic();
528 			break;
529 		case CA_DISCONNECTED:
530 			// force menu up
531 			S_StopAllSounds();
532 			VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
533 			break;
534 		case CA_CONNECTING:
535 		case CA_CHALLENGING:
536 		case CA_CONNECTED:
537 			// connecting clients will only show the connection dialog
538 			// refresh to update the time
539 			VM_Call( uivm, UI_REFRESH, cls.realtime );
540 			VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
541 			break;
542 //			// Ridah, if the cgame is valid, fall through to there
543 //			if (!cls.cgameStarted || !com_sv_running->integer) {
544 //				// connecting clients will only show the connection dialog
545 //				VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qfalse );
546 //				break;
547 //			}
548 		case CA_LOADING:
549 		case CA_PRIMED:
550 			// draw the game information screen and loading progress
551 			CL_CGameRendering( stereoFrame );
552 
553 			// also draw the connection information, so it doesn't
554 			// flash away too briefly on local or lan games
555 			//if (!com_sv_running->value || Cvar_VariableIntegerValue("sv_cheats"))	// Ridah, don't draw useless text if not in dev mode
556 			VM_Call( uivm, UI_REFRESH, cls.realtime );
557 			VM_Call( uivm, UI_DRAW_CONNECT_SCREEN, qtrue );
558 			break;
559 		case CA_ACTIVE:
560 			// always supply STEREO_CENTER as vieworg offset is now done by the engine.
561 			CL_CGameRendering( stereoFrame );
562 			SCR_ShowPing();
563 			SCR_DrawDemoRecording();
564 #ifdef USE_VOIP
565 			SCR_DrawVoipMeter();
566 #endif
567 			break;
568 		}
569 	}
570 
571 	// the menu draws next
572 	if ( Key_GetCatcher( ) & KEYCATCH_UI && uivm ) {
573 		VM_Call( uivm, UI_REFRESH, cls.realtime );
574 	}
575 
576 	// console draws next
577 	Con_DrawConsole();
578 
579 	// debug graph can be drawn on top of anything
580 	if ( cl_debuggraph->integer || cl_timegraph->integer || cl_debugMove->integer ) {
581 		SCR_DrawDebugGraph();
582 	}
583 }
584 
585 /*
586 ==================
587 SCR_UpdateScreen
588 
589 This is called every frame, and can also be called explicitly to flush
590 text to the screen.
591 ==================
592 */
SCR_UpdateScreen(void)593 void SCR_UpdateScreen( void ) {
594 	static int recursive;
595 
596 	if ( !scr_initialized ) {
597 		return;             // not initialized yet
598 	}
599 
600 	if ( ++recursive > 2 ) {
601 		Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
602 	}
603 	recursive = 1;
604 
605 	// If there is no VM, there are also no rendering commands issued. Stop the renderer in
606 	// that case.
607 	if( uivm || com_dedicated->integer )
608 	{
609 		// XXX
610 		int in_anaglyphMode = Cvar_VariableIntegerValue("r_anaglyphMode");
611 		// if running in stereo, we need to draw the frame twice
612 		if ( cls.glconfig.stereoEnabled || in_anaglyphMode) {
613 			SCR_DrawScreenField( STEREO_LEFT );
614 			SCR_DrawScreenField( STEREO_RIGHT );
615 		} else {
616 			SCR_DrawScreenField( STEREO_CENTER );
617 		}
618 
619 		if ( com_speeds->integer ) {
620 			re.EndFrame( &time_frontend, &time_backend );
621 		} else {
622 			re.EndFrame( NULL, NULL );
623 		}
624 	}
625 
626 	recursive = 0;
627 }
628