1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
21 
22 #include "client.h"
23 
24 qboolean	scr_initialized;		// ready to draw
25 
26 vrect_t		scr_vrect;		// position of render window on screen
27 
28 glconfig_t	scr_glconfig;
29 
30 
31 cvar_t		*scr_viewsize;
32 cvar_t		*scr_centertime;
33 cvar_t		*scr_showpause;
34 cvar_t		*scr_printspeed;
35 
36 cvar_t		*scr_netgraph;
37 cvar_t		*scr_timegraph;
38 cvar_t		*scr_debuggraph;
39 cvar_t		*scr_graphheight;
40 cvar_t		*scr_graphscale;
41 cvar_t		*scr_graphshift;
42 cvar_t		*scr_demobar;
43 cvar_t		*scr_fontvar;
44 cvar_t		*scr_scale;
45 
46 cvar_t		*crosshair;
47 
48 qhandle_t	crosshair_pic;
49 int			crosshair_width, crosshair_height;
50 
51 qhandle_t	scr_backtile;
52 qhandle_t	scr_pause;
53 qhandle_t	scr_net;
54 qhandle_t	scr_font;
55 
56 int			scr_hudWidth;
57 int			scr_hudHeight;
58 
59 #define STAT_MINUS		10	// num frame for '-' stats digit
60 
61 static const char	*sb_nums[2][11] = {
62 	{ "num_0", "num_1", "num_2", "num_3", "num_4", "num_5",
63 	"num_6", "num_7", "num_8", "num_9", "num_minus" },
64 	{ "anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
65 	"anum_6", "anum_7", "anum_8", "anum_9", "anum_minus" }
66 };
67 
68 static qhandle_t	sb_pics[2][11];
69 static qhandle_t	sb_inventory;
70 static qhandle_t	sb_field;
71 
72 #define	ICON_WIDTH	24
73 #define	ICON_HEIGHT	24
74 #define	CHAR_WIDTH	16
75 #define	ICON_SPACE	8
76 
77 void SCR_TimeRefresh_f (void);
78 void SCR_Loading_f (void);
79 
80 
81 /*
82 ===============================================================================
83 
84 BAR GRAPHS
85 
86 ===============================================================================
87 */
88 
89 /*
90 ==============
91 CL_AddNetgraph
92 
93 A new packet was just parsed
94 ==============
95 */
CL_AddNetgraph(void)96 void CL_AddNetgraph (void)
97 {
98 	int		i;
99 	int		in;
100 	int		ping;
101 
102 	// if using the debuggraph for something else, don't
103 	// add the net lines
104 	if (scr_debuggraph->integer || scr_timegraph->integer)
105 		return;
106 
107 	for (i=0 ; i<cls.netchan->dropped ; i++)
108 		SCR_DebugGraph (30, 0x40);
109 
110 	for (i=0 ; i<cl.surpressCount ; i++)
111 		SCR_DebugGraph (30, 0xdf);
112 
113 	// see what the latency was on this packet
114 	in = cls.netchan->incoming_acknowledged & CMD_MASK;
115 	ping = cls.realtime - cl.history[in].realtime;
116 	ping /= 30;
117 	if (ping > 30)
118 		ping = 30;
119 	SCR_DebugGraph (ping, 0xd0);
120 }
121 
122 
123 typedef struct
124 {
125 	float	value;
126 	int		color;
127 } graphsamp_t;
128 
129 static	int			current;
130 static	graphsamp_t	values[1024];
131 
132 /*
133 ==============
134 SCR_DebugGraph
135 ==============
136 */
SCR_DebugGraph(float value,int color)137 void SCR_DebugGraph (float value, int color)
138 {
139 	values[current&1023].value = value;
140 	values[current&1023].color = color;
141 	current++;
142 }
143 
144 /*
145 ==============
146 SCR_DrawDebugGraph
147 ==============
148 */
SCR_DrawDebugGraph(void)149 void SCR_DrawDebugGraph (void)
150 {
151 	int		a, x, y, w, i, h;
152 	float	v;
153 	int		color;
154 
155 	//
156 	// draw the graph
157 	//
158 	w = scr_vrect.width;
159 
160 	x = scr_vrect.x;
161 	y = scr_vrect.y+scr_vrect.height;
162 	ref.DrawFill (x, y-scr_graphheight->value,
163 		w, scr_graphheight->value, 8);
164 
165 	for (a=0 ; a<w ; a++)
166 	{
167 		i = (current-1-a+1024) & 1023;
168 		v = values[i].value;
169 		color = values[i].color;
170 		v = v*scr_graphscale->value + scr_graphshift->value;
171 
172 		if (v < 0)
173 			v += scr_graphheight->value * (1+(int)(-v/scr_graphheight->value));
174 		h = (int)v % (int)scr_graphheight->value;
175 		ref.DrawFill (x+w-1-a, y - h, 1,	h, color);
176 	}
177 }
178 
SCR_DrawPercentBar(int percent)179 static void SCR_DrawPercentBar( int percent ) {
180 	char buffer[8];
181 	int x, w;
182 	int length;
183 
184 	scr_hudHeight -= TINYCHAR_HEIGHT;
185 
186 	w = scr_hudWidth * percent / 100;
187 
188 	ref.DrawFill( 0, scr_hudHeight, w, TINYCHAR_HEIGHT, 4 );
189 	ref.DrawFill( w, scr_hudHeight, scr_hudWidth - w, TINYCHAR_HEIGHT, 0 );
190 
191 	length = Com_sprintf( buffer, sizeof( buffer ), "%d%%", percent );
192 	x = ( scr_hudWidth - length * TINYCHAR_WIDTH ) / 2;
193 	ref.DrawString( x, scr_hudHeight, 0, MAX_STRING_CHARS, buffer, scr_font );
194 }
195 
196 /*
197 ================
198 SCR_DrawDemoBar
199 ================
200 */
SCR_DrawDemoBar(void)201 static void SCR_DrawDemoBar( void ) {
202 	int percent, bufferPercent;
203 
204 	if( !scr_demobar->integer ) {
205 		return;
206 	}
207 
208 	if( cls.demoplayback ) {
209 		SCR_DrawPercentBar( cls.demofilePercent );
210 		return;
211 	}
212 
213 	if( sv_running->integer != ss_broadcast ) {
214 		return;
215 	}
216 
217 	if(	!MVD_GetDemoPercent( &percent, &bufferPercent ) ) {
218 		return;
219 	}
220 
221 	if( scr_demobar->integer & 1 ) {
222 		SCR_DrawPercentBar( percent );
223 	}
224 	if( scr_demobar->integer & 2 ) {
225 		SCR_DrawPercentBar( bufferPercent );
226 	}
227 
228 }
229 
230 /*
231 ===============================================================================
232 
233 CENTER PRINTING
234 
235 ===============================================================================
236 */
237 
238 static char		scr_centerstring[MAX_STRING_CHARS];
239 static int		scr_centertime_start;	// for slow victory printing
240 static int		scr_center_lines;
241 
242 /*
243 ==============
244 SCR_CenterPrint
245 
246 Called for important messages that should stay in the center of the screen
247 for a few moments
248 ==============
249 */
SCR_CenterPrint(const char * str)250 void SCR_CenterPrint( const char *str ) {
251 	const char	*s;
252 
253 	scr_centertime_start = cls.realtime;
254     if( !strcmp( scr_centerstring, str ) ) {
255         return;
256     }
257 
258 	Q_strncpyz( scr_centerstring, str, sizeof( scr_centerstring ) );
259 
260 	// count the number of lines for centering
261 	scr_center_lines = 1;
262 	s = str;
263 	while( *s )	{
264 		if( *s == '\n' )
265 			scr_center_lines++;
266 		s++;
267 	}
268 
269 	// echo it to the console
270 	Com_Printf( "%s\n", scr_centerstring );
271 	Con_ClearNotify_f();
272 }
273 
SCR_DrawCenterString(void)274 void SCR_DrawCenterString( void ) {
275 	int y;
276 	float alpha;
277 
278 	Cvar_ClampValue( scr_centertime, 0.3f, 10.0f );
279 
280 	alpha = SCR_FadeAlpha( scr_centertime_start, scr_centertime->value * 1000, 300 );
281 	if( !alpha ) {
282 		return;
283 	}
284 
285 	ref.SetColor( DRAW_COLOR_ALPHA, ( byte * )&alpha );
286 
287 	y = scr_hudHeight / 4 - scr_center_lines * 8 / 2;
288 
289 	UIS_DrawStringEx( scr_hudWidth / 2, y, UI_CENTER|UI_MULTILINE,
290 		MAX_STRING_CHARS, scr_centerstring, scr_font );
291 
292 	ref.SetColor( DRAW_COLOR_CLEAR, NULL );
293 }
294 
295 //============================================================================
296 
297 /*
298 =================
299 SCR_CalcVrect
300 
301 Sets scr_vrect, the coordinates of the rendered window
302 =================
303 */
SCR_CalcVrect(void)304 static void SCR_CalcVrect( void ) {
305 	int		size;
306 
307 	// bound viewsize
308 	Cvar_ClampInteger( scr_viewsize, 40, 100 );
309 	scr_viewsize->modified = qfalse;
310 
311 	size = scr_viewsize->integer;
312 
313 	scr_vrect.width = scr_hudWidth * size / 100;
314 	scr_vrect.width &= ~7;
315 
316 	scr_vrect.height = scr_hudHeight * size / 100;
317 	scr_vrect.height &= ~1;
318 
319 	scr_vrect.x = ( scr_hudWidth - scr_vrect.width ) / 2;
320 	scr_vrect.y = ( scr_hudHeight - scr_vrect.height ) / 2;
321 }
322 
323 /*
324 =================
325 SCR_SizeUp_f
326 
327 Keybinding command
328 =================
329 */
SCR_SizeUp_f(void)330 void SCR_SizeUp_f (void)
331 {
332 	Cvar_SetInteger("viewsize",scr_viewsize->integer+10);
333 }
334 
335 
336 /*
337 =================
338 SCR_SizeDown_f
339 
340 Keybinding command
341 =================
342 */
SCR_SizeDown_f(void)343 void SCR_SizeDown_f (void)
344 {
345 	Cvar_SetInteger ("viewsize",scr_viewsize->integer-10);
346 }
347 
348 /*
349 =================
350 SCR_Sky_f
351 
352 Set a specific sky and rotation speed
353 =================
354 */
SCR_Sky_f(void)355 void SCR_Sky_f (void)
356 {
357 	float	rotate;
358 	vec3_t	axis;
359 
360 	if (Cmd_Argc() < 2)
361 	{
362 		Com_Printf ("Usage: sky <basename> [rotate] [axis x y z]\n");
363 		return;
364 	}
365 	if (Cmd_Argc() > 2)
366 		rotate = atof(Cmd_Argv(2));
367 	else
368 		rotate = 0;
369 	if (Cmd_Argc() == 6)
370 	{
371 		axis[0] = atof(Cmd_Argv(3));
372 		axis[1] = atof(Cmd_Argv(4));
373 		axis[2] = atof(Cmd_Argv(5));
374 	}
375 	else
376 	{
377 		axis[0] = 0;
378 		axis[1] = 0;
379 		axis[2] = 1;
380 	}
381 
382 	ref.SetSky (Cmd_Argv(1), rotate, axis);
383 }
384 
385 //============================================================================
386 
387 /*
388 ===============
389 SCR_TouchPics
390 
391 Allows rendering code to cache all needed sbar graphics
392 ===============
393 */
SCR_TouchPics(void)394 void SCR_TouchPics( void ) {
395 	int		i, j;
396 	char	buffer[16];
397 
398 	for( i = 0; i < 2; i++ )
399 		for( j = 0; j < 11; j++ )
400 			sb_pics[i][j] = ref.RegisterPic( sb_nums[i][j] );
401 
402 	sb_inventory = ref.RegisterPic( "inventory" );
403 	sb_field = ref.RegisterPic( "field_3" );
404 
405 	if( crosshair->integer ) {
406 		if( crosshair->integer < 0 ) {
407 			Cvar_SetInteger( "crosshair", 0 );
408 		}
409 
410 		Com_sprintf( buffer, sizeof( buffer ), "ch%i", crosshair->integer );
411 		crosshair_pic = ref.RegisterPic( buffer );
412 		ref.DrawGetPicSize( &crosshair_width, &crosshair_height,
413                 crosshair_pic );
414 	}
415 }
416 
417 /*
418 ==================
419 SCR_RegisterMedia
420 ==================
421 */
SCR_RegisterMedia(void)422 void SCR_RegisterMedia( void ) {
423 	ref.GetConfig( &scr_glconfig );
424 
425 	scr_backtile = ref.RegisterPic( "backtile" );
426 	scr_pause = ref.RegisterPic( "pause" );
427 	scr_net = ref.RegisterPic( "net" );
428 	scr_font = ref.RegisterFont( scr_fontvar->string );
429 }
430 
SCR_FontImage_OnChange(cvar_t * self,void * arg)431 static void SCR_FontImage_OnChange( cvar_t *self, void *arg ) {
432 	scr_font = ref.RegisterFont( self->string );
433 }
434 
435 /*
436 ==================
437 SCR_Init
438 ==================
439 */
SCR_Init(void)440 void SCR_Init (void)
441 {
442 	scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE);
443 	scr_showpause = Cvar_Get ("scr_showpause", "1", 0);
444 	scr_centertime = Cvar_Get ("scr_centertime", "2.5", 0);
445 	scr_printspeed = Cvar_Get ("scr_printspeed", "8", 0);
446 	scr_netgraph = Cvar_Get ("netgraph", "0", 0);
447 	scr_timegraph = Cvar_Get ("timegraph", "0", 0);
448 	scr_debuggraph = Cvar_Get ("debuggraph", "0", 0);
449 	scr_graphheight = Cvar_Get ("graphheight", "32", 0);
450 	scr_graphscale = Cvar_Get ("graphscale", "1", 0);
451 	scr_graphshift = Cvar_Get ("graphshift", "0", 0);
452 	scr_demobar = Cvar_Get( "scr_demobar", "1", CVAR_ARCHIVE );
453 	scr_fontvar = Cvar_Get( "scr_font", "conchars", CVAR_ARCHIVE );
454 	scr_fontvar->changedFunc = SCR_FontImage_OnChange;
455 	scr_scale = Cvar_Get( "scr_scale", "1", CVAR_ARCHIVE );
456 
457 	SCR_InitDraw();
458 
459 //
460 // register our commands
461 //
462 	Cmd_AddCommand ("timerefresh",SCR_TimeRefresh_f);
463 	Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
464 	Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
465 	Cmd_AddCommand ("sky",SCR_Sky_f);
466 
467 	scr_glconfig.vidWidth = 320;
468 	scr_glconfig.vidHeight = 240;
469 
470 	scr_initialized = qtrue;
471 }
472 
473 
474 
475 
476 /*
477 ==============
478 SCR_DrawPause
479 ==============
480 */
SCR_DrawPause(void)481 void SCR_DrawPause( void ) {
482 	int		x, y, w, h;
483 
484 	if( !sv_paused->integer ) {
485 		return;
486 	}
487 
488 	if( !scr_showpause->integer ) {		// turn off for screenshots
489 		return;
490 	}
491 
492 	if( cls.key_dest & KEY_MENU ) {
493 		return;
494 	}
495 
496 	ref.DrawGetPicSize( &w, &h, scr_pause );
497     x = ( scr_glconfig.vidWidth - w ) / 2;
498     y = scr_glconfig.vidHeight / 2 + 8;
499 	ref.DrawPic( x, y, scr_pause );
500 }
501 
502 //=============================================================================
503 
504 /*
505 ================
506 SCR_BeginLoadingPlaque
507 ================
508 */
SCR_BeginLoadingPlaque(void)509 void SCR_BeginLoadingPlaque( void ) {
510 	Con_Close();
511 	UI_OpenMenu( UIMENU_NONE );
512 	S_StopAllSounds();
513 	CDAudio_Stop();
514 }
515 
516 /*
517 ================
518 SCR_EndLoadingPlaque
519 ================
520 */
SCR_EndLoadingPlaque(void)521 void SCR_EndLoadingPlaque( void ) {
522 	Con_ClearNotify_f();
523 }
524 
525 
526 /*
527 ================
528 SCR_TimeRefresh_f
529 ================
530 */
SCR_TimeRefresh_f(void)531 void SCR_TimeRefresh_f (void)
532 {
533 	int		i;
534 	int		start, stop;
535 	float	time;
536 
537 	if ( cls.state != ca_active )
538 		return;
539 
540 	start = Sys_Milliseconds ();
541 
542 	if (Cmd_Argc() == 2) {
543 		// run without page flipping
544 		ref.BeginFrame( 0 );
545 		for (i=0 ; i<128 ; i++) {
546 			cl.refdef.viewangles[1] = i/128.0*360.0;
547 			ref.RenderFrame (&cl.refdef);
548 		}
549 		ref.EndFrame();
550 	} else {
551 		for (i=0 ; i<128 ; i++) {
552 			cl.refdef.viewangles[1] = i/128.0*360.0;
553 
554 			ref.BeginFrame( 0 );
555 			ref.RenderFrame (&cl.refdef);
556 			ref.EndFrame();
557 		}
558 	}
559 
560 	stop = Sys_Milliseconds ();
561 	time = (stop-start)/1000.0;
562 	Com_Printf ("%f seconds (%f fps)\n", time, 128/time);
563 }
564 
565 
566 
567 /*
568 ==============
569 SCR_TileClear
570 
571 Clear any parts of the tiled background that were drawn on last frame
572 ==============
573 */
SCR_TileClear(void)574 void SCR_TileClear( void ) {
575 	int	top, bottom, left, right;
576 
577 	//if( con.currentHeight == 1 )
578 	//	return;		// full screen console
579 
580 	if( scr_viewsize->integer == 100 )
581 		return;		// full screen rendering
582 
583 	if( cl.cinematictime > 0 )
584 		return;		// full screen cinematic
585 
586 	top = scr_vrect.y;
587 	bottom = top + scr_vrect.height - 1;
588 	left = scr_vrect.x;
589 	right = left + scr_vrect.width - 1;
590 
591 
592 	// clear above view screen
593 	ref.DrawTileClear( 0, 0, scr_glconfig.vidWidth, top, scr_backtile );
594 
595 	// clear below view screen
596 	ref.DrawTileClear( 0, bottom, scr_glconfig.vidWidth,
597             scr_glconfig.vidHeight - bottom, scr_backtile );
598 
599 	// clear left of view screen
600 	ref.DrawTileClear( 0, top, left, scr_vrect.height, scr_backtile );
601 
602 	// clear right of view screen
603 	ref.DrawTileClear( right, top, scr_glconfig.vidWidth - right,
604             scr_vrect.height, scr_backtile );
605 
606 }
607 
608 
609 /*
610 ===============================================================================
611 
612 STAT PROGRAMS
613 
614 ===============================================================================
615 */
616 
617 #define HUD_DrawString( x, y, string ) \
618     ref.DrawString( x, y, 0, MAX_STRING_CHARS, string, scr_font )
619 
620 #define HUD_DrawAltString( x, y, string ) \
621     ref.DrawString( x, y, UI_ALTCOLOR, MAX_STRING_CHARS, string, scr_font )
622 
623 #define HUD_DrawCenterString( x, y, string ) \
624     UIS_DrawStringEx( x, y, UI_CENTER|UI_MULTILINE, MAX_STRING_CHARS, string, scr_font )
625 
626 #define HUD_DrawAltCenterString( x, y, string ) \
627     UIS_DrawStringEx( x, y, UI_CENTER|UI_MULTILINE|UI_ALTCOLOR, MAX_STRING_CHARS, string, scr_font )
628 
629 
630 
631 /*
632 ==============
633 HUD_DrawNumber
634 ==============
635 */
HUD_DrawNumber(int x,int y,int color,int width,int value)636 void HUD_DrawNumber( int x, int y, int color, int width, int value ) {
637 	char	num[16], *ptr;
638 	int		l;
639 	int		frame;
640 
641 	if( width < 1 )
642 		return;
643 
644 	// draw number string
645 	if( width > 5 )
646 		width = 5;
647 
648 	color &= 1;
649 
650 	Com_sprintf( num, sizeof( num ), "%i", value );
651 	l = strlen( num );
652 	if( l > width )
653 		l = width;
654 	x += 2 + CHAR_WIDTH * ( width - l );
655 
656 	ptr = num;
657 	while( *ptr && l ) {
658 		if( *ptr == '-' )
659 			frame = STAT_MINUS;
660 		else
661 			frame = *ptr - '0';
662 
663 		ref.DrawPic( x, y, sb_pics[color][frame] );
664 		x += CHAR_WIDTH;
665 		ptr++;
666 		l--;
667 	}
668 }
669 
670 /*
671 ================
672 CL_DrawInventory
673 ================
674 */
675 #define	DISPLAY_ITEMS	17
676 
SCR_DrawInventory(void)677 void SCR_DrawInventory( void ) {
678 	int		i;
679 	int		num, selected_num, item;
680 	int		index[MAX_ITEMS];
681 	char	string[MAX_STRING_CHARS];
682 	int		x, y;
683 	char	*bind;
684 	int		selected;
685 	int		top;
686 
687 	selected = cl.frame.ps.ps.stats[STAT_SELECTED_ITEM];
688 
689 	num = 0;
690 	selected_num = 0;
691 	for( i = 0; i < MAX_ITEMS; i++ ) {
692 		if( i == selected ) {
693 			selected_num = num;
694 		}
695 		if( cl.inventory[i] ) {
696 			index[num++] = i;
697 		}
698 	}
699 
700 	// determine scroll point
701 	top = selected_num - DISPLAY_ITEMS / 2;
702 	clamp( top, 0, num - DISPLAY_ITEMS );
703 
704 	x = ( scr_hudWidth - 256 ) / 2;
705 	y = ( scr_hudHeight - 240 ) / 2;
706 
707 	ref.DrawPic( x, y + 8, sb_inventory );
708 	y += 24;
709 	x += 24;
710 
711 	HUD_DrawString( x, y, "hotkey ### item" );
712 	y += TINYCHAR_HEIGHT;
713 
714 	HUD_DrawString( x, y, "------ --- ----" );
715 	y += TINYCHAR_HEIGHT;
716 
717 	for( i = top; i < num && i < top + DISPLAY_ITEMS; i++ ) {
718 		item = index[i];
719 		// search for a binding
720 		Com_sprintf( string, sizeof( string ), "use %s",
721                 cl.configstrings[CS_ITEMS + item] );
722 		bind = Key_GetBinding( string );
723 
724 		Com_sprintf( string, sizeof( string ), "%6s %3i %s",
725                 bind, cl.inventory[item], cl.configstrings[CS_ITEMS + item] );
726 
727 		if( item != selected ) {
728 			HUD_DrawAltString( x, y, string );
729 		} else {	// draw a blinky cursor by the selected item
730 			HUD_DrawString( x, y, string );
731 			if( ( cls.realtime >> 8 ) & 1 ) {
732 				ref.DrawChar( x - TINYCHAR_WIDTH, y, 0, 15, scr_font );
733 			}
734 
735 		}
736 
737 		y += TINYCHAR_HEIGHT;
738 	}
739 
740 
741 }
742 
743 /*
744 ================
745 SCR_ExecuteLayoutString
746 
747 ================
748 */
SCR_ExecuteLayoutString(const char * s)749 void SCR_ExecuteLayoutString( const char *s ) {
750     char	buffer[80];
751 	int		x, y;
752 	int		value;
753 	char	*token;
754 	int		width;
755 	int		index;
756 	clientinfo_t	*ci;
757 
758 	if( !s[0] )
759 		return;
760 
761 	x = 0;
762 	y = 0;
763 	width = 3;
764 
765 	while( s ) {
766 		token = COM_Parse( &s );
767 		if( token[2] == 0 ) {
768 			if( token[0] == 'x' ) {
769 				if( token[1] == 'l' ) {
770 					token = COM_Parse( &s );
771 					x = atoi( token );
772 					continue;
773 				}
774 
775 				if( token[1] == 'r' ) {
776 					token = COM_Parse( &s );
777 					x = scr_hudWidth + atoi( token );
778 					continue;
779 				}
780 
781 				if( token[1] == 'v' ) {
782 					token = COM_Parse( &s );
783 					x = scr_hudWidth / 2 - 160 + atoi( token );
784 					continue;
785 				}
786 			}
787 
788 			if( token[0] == 'y' ) {
789 				if( token[1] == 't' ) {
790 					token = COM_Parse( &s );
791 					y = atoi( token );
792 					continue;
793 				}
794 
795 				if( token[1] == 'b' ) {
796 					token = COM_Parse( &s );
797 					y = scr_hudHeight + atoi( token );
798 					continue;
799 				}
800 
801 				if( token[1] == 'v' ) {
802 					token = COM_Parse( &s );
803 					y = scr_hudHeight / 2 - 120 + atoi( token );
804 					continue;
805 				}
806 			}
807 		}
808 
809 		if( !strcmp( token, "pic" ) ) {
810 			// draw a pic from a stat number
811 			token = COM_Parse( &s );
812 			value = atoi( token );
813 			if( value < 0 || value >= MAX_STATS ) {
814 				Com_Error( ERR_DROP,
815                         "SCR_ExecuteLayoutString: invalid pic index" );
816 			}
817 			value = cl.frame.ps.ps.stats[value];
818 			if( value < 0 || value >= MAX_IMAGES ) {
819 				Com_Error( ERR_DROP,
820                         "SCR_ExecuteLayoutString: invalid pic index" );
821 			}
822 			token = cl.configstrings[CS_IMAGES + value];
823 			if( token[0] ) {
824 				UIS_DrawPicByName( x, y, token );
825 			}
826 			continue;
827 		}
828 
829 		if( !strcmp( token, "client" ) ) {
830 			// draw a deathmatch client block
831 			int		score, ping, time;
832 
833 			token = COM_Parse( &s );
834 			x = scr_hudWidth / 2 - 160 + atoi( token );
835 			token = COM_Parse( &s );
836 			y = scr_hudHeight / 2 - 120 + atoi( token );
837 
838 			token = COM_Parse( &s );
839 			value = atoi( token );
840 			if( value < 0 || value >= MAX_CLIENTS ) {
841 				Com_Error( ERR_DROP,
842                         "SCR_ExecuteLayoutString: invalid client index" );
843 			}
844 			ci = &cl.clientinfo[value];
845 
846 			token = COM_Parse( &s );
847 			score = atoi( token );
848 
849 			token = COM_Parse( &s );
850 			ping = atoi( token );
851 
852 			token = COM_Parse( &s );
853 			time = atoi( token );
854 
855 			HUD_DrawString( x + 32, y, ci->name );
856             Com_sprintf( buffer, sizeof( buffer ), "Score: %i", score );
857 			HUD_DrawString( x + 32, y + TINYCHAR_HEIGHT, buffer );
858             Com_sprintf( buffer, sizeof( buffer ), "Ping:  %i", ping );
859 			HUD_DrawString( x + 32, y + 2 * TINYCHAR_HEIGHT, buffer );
860             Com_sprintf( buffer, sizeof( buffer ), "Time:  %i", time );
861 			HUD_DrawString( x + 32, y + 3 * TINYCHAR_HEIGHT, buffer );
862 
863 			if( !ci->icon ) {
864 				ci = &cl.baseclientinfo;
865 			}
866 			ref.DrawPic( x, y, ci->icon );
867 			continue;
868 		}
869 
870 		if( !strcmp( token, "ctf" ) ) {
871 			// draw a ctf client block
872 			int		score, ping;
873 
874 			token = COM_Parse( &s );
875 			x = scr_hudWidth / 2 - 160 + atoi( token );
876 			token = COM_Parse( &s );
877 			y = scr_hudHeight / 2 - 120 + atoi( token );
878 
879 			token = COM_Parse( &s );
880 			value = atoi( token );
881 			if( value < 0 || value >= MAX_CLIENTS ) {
882 				Com_Error( ERR_DROP,
883                         "SCR_ExecuteLayoutString: invalid client index" );
884 			}
885 			ci = &cl.clientinfo[value];
886 
887 			token = COM_Parse( &s );
888 			score = atoi( token );
889 
890 			token = COM_Parse( &s );
891 			ping = atoi( token );
892 			if( ping > 999 )
893 				ping = 999;
894 
895 			Com_sprintf( buffer, sizeof( buffer ), "%3d %3d %-12.12s",
896                     score, ping, ci->name );
897             if( value == cl.frame.ps.clientNum ) {
898 				HUD_DrawAltString( x, y, buffer );
899 			} else {
900 				HUD_DrawString( x, y, buffer );
901 			}
902 			continue;
903 		}
904 
905 		if( !strcmp( token, "picn" ) ) {
906 			// draw a pic from a name
907 			token = COM_Parse( &s );
908 			UIS_DrawPicByName( x, y, token );
909 			continue;
910 		}
911 
912 		if( !strcmp( token, "num" ) ) {
913 			// draw a number
914 			token = COM_Parse( &s );
915 			width = atoi( token );
916 			token = COM_Parse( &s );
917 			value = atoi( token );
918 			if( value < 0 || value >= MAX_STATS ) {
919 				Com_Error( ERR_DROP,
920                         "SCR_ExecuteLayoutString: invalid stat index" );
921 			}
922 			value = cl.frame.ps.ps.stats[value];
923 			HUD_DrawNumber( x, y, 0, width, value );
924 			continue;
925 		}
926 
927 		if( !strcmp( token, "hnum" ) ) {
928 			// health number
929 			int		color;
930 
931 			width = 3;
932 			value = cl.frame.ps.ps.stats[STAT_HEALTH];
933 			if( value > 25 )
934 				color = 0;	// green
935 			else if( value > 0 )
936 				color = ( cl.frame.serverFrame >> 2 ) & 1;		// flash
937 			else
938 				color = 1;
939 
940 			if( cl.frame.ps.ps.stats[STAT_FLASHES] & 1 )
941 				ref.DrawPic( x, y, sb_field );
942 
943 			HUD_DrawNumber( x, y, color, width, value );
944 			continue;
945 		}
946 
947 		if( !strcmp( token, "anum" ) ) {
948 			// ammo number
949 			int		color;
950 
951 			width = 3;
952 			value = cl.frame.ps.ps.stats[STAT_AMMO];
953 			if( value > 5 )
954 				color = 0;	// green
955 			else if( value >= 0 )
956 				color = ( cl.frame.serverFrame >> 2 ) & 1;		// flash
957 			else
958 				continue;	// negative number = don't show
959 
960 			if( cl.frame.ps.ps.stats[STAT_FLASHES] & 4 )
961 				ref.DrawPic( x, y, sb_field );
962 
963 			HUD_DrawNumber( x, y, color, width, value );
964 			continue;
965 		}
966 
967 		if( !strcmp( token, "rnum" ) ) {
968 			// armor number
969 			int		color;
970 
971 			width = 3;
972 			value = cl.frame.ps.ps.stats[STAT_ARMOR];
973 			if( value < 1 )
974 				continue;
975 
976 			color = 0;	// green
977 
978 			if( cl.frame.ps.ps.stats[STAT_FLASHES] & 2 )
979 				ref.DrawPic( x, y, sb_field );
980 
981 			HUD_DrawNumber( x, y, color, width, value );
982 			continue;
983 		}
984 
985 
986 		if( !strcmp( token, "stat_string" ) ) {
987 			token = COM_Parse( &s );
988 			index = atoi( token );
989 			if( index < 0 || index >= MAX_STATS ) {
990 				Com_Error( ERR_DROP, "SCR_ExecuteLayoutString: "
991 					"invalid stat_string index" );
992 			}
993 			index = cl.frame.ps.ps.stats[index];
994 			if( index < 0 || index >= MAX_CONFIGSTRINGS ) {
995 				Com_Error( ERR_DROP, "SCR_ExecuteLayoutString: "
996 					"invalid stat_string index" );
997 			}
998 			HUD_DrawString( x, y, cl.configstrings[index] );
999 			continue;
1000 		}
1001 
1002 		if( !strcmp( token, "cstring" ) ) {
1003 			token = COM_Parse( &s );
1004 			HUD_DrawCenterString( x + 320 / 2, y, token );
1005 			continue;
1006 		}
1007 
1008 		if( !strcmp( token, "cstring2" ) ) {
1009 			token = COM_Parse( &s );
1010 			HUD_DrawAltCenterString( x + 320 / 2, y, token );
1011 			continue;
1012 		}
1013 
1014 		if( !strcmp( token, "string" ) ) {
1015 			token = COM_Parse( &s );
1016 			HUD_DrawString( x, y, token );
1017 			continue;
1018 		}
1019 
1020 		if( !strcmp( token, "string2" ) ) {
1021 			token = COM_Parse( &s );
1022 			HUD_DrawAltString( x, y, token );
1023 			continue;
1024 		}
1025 
1026 		if( !strcmp( token, "if" ) ) {
1027 			token = COM_Parse( &s );
1028 			value = atoi( token );
1029 			if( value < 0 || value >= MAX_STATS ) {
1030 				Com_Error( ERR_DROP, "SCR_ExecuteLayoutString: "
1031 					"invalid stat index" );
1032 			}
1033 			value = cl.frame.ps.ps.stats[value];
1034 			if( !value ) {	// skip to endif
1035 				while( strcmp( token, "endif" ) ) {
1036 					token = COM_Parse( &s );
1037 					if( !s ) {
1038 						break;
1039 					}
1040 				}
1041 			}
1042 
1043 			continue;
1044 		}
1045 
1046 
1047 	}
1048 }
1049 
SCR_DrawActiveFrame(float separation)1050 static void SCR_DrawActiveFrame( float separation ) {
1051 	float scale;
1052 
1053 	scr_hudHeight = scr_glconfig.vidHeight;
1054 	scr_hudWidth = scr_glconfig.vidWidth;
1055 
1056 	SCR_DrawDemoBar();
1057 
1058 	SCR_CalcVrect();
1059 
1060 	/* clear any dirty part of the background */
1061 	SCR_TileClear();
1062 
1063 	/* draw 3D game view */
1064 	V_RenderView( separation );
1065 
1066 	Cvar_ClampValue( scr_scale, 1, 9 );
1067 
1068 	scale = 1.0f / scr_scale->value;
1069 	ref.SetScale( &scale );
1070 
1071 	scr_hudHeight *= scale;
1072 	scr_hudWidth *= scale;
1073 
1074 	/* draw all 2D elements */
1075 	SCR_Draw2D();
1076 
1077 	ref.SetScale( NULL );
1078 }
1079 
1080 #if 0
1081 static void PlayerConfig_MenuDraw( void ) {
1082 	refdef_t refdef;
1083 	char scratch[MAX_QPATH];
1084 	entity_t entity[2];
1085 
1086 	memset( &refdef, 0, sizeof( refdef ) );
1087 
1088 	refdef.x = SCREEN_WIDTH / 2;
1089 	refdef.y = 60;
1090 	refdef.width = SCREEN_WIDTH / 2;
1091 	refdef.height = SCREEN_HEIGHT - 122;
1092 
1093 	refdef.fov_x = 40;
1094 	refdef.fov_y = Com_CalcFov( refdef.fov_x, refdef.width, refdef.height );
1095 	refdef.time = cls.realtime * 0.001f;
1096 
1097 	memset( entity, 0, sizeof( entity ) );
1098 
1099 	Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", "male" );
1100 	entity[0].model = ref.RegisterModel( scratch );
1101 	Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", "male", "flak" );
1102 	entity[0].skin = ref.RegisterSkin( scratch );
1103 	entity[0].flags = RF_FULLBRIGHT;
1104 	entity[0].origin[0] = 80;
1105 	entity[0].origin[1] = 0;
1106 	entity[0].origin[2] = 0;
1107 	VectorCopy( entity[0].origin, entity[0].oldorigin );
1108 	entity[0].frame = 0;
1109 	entity[0].oldframe = 0;
1110 	entity[0].backlerp = 0.0;
1111 	entity[0].angles[1] = anglemod( cls.realtime / 20.0f );
1112 
1113 	entity[1] = entity[0];
1114 	entity[1].skin = 0;
1115 	entity[1].skinnum = 0;
1116 	entity[1].alpha = 0;
1117 	Com_sprintf( scratch, sizeof( scratch ), "players/%s/w_railgun.md2", "male" );
1118 	entity[1].model = ref.RegisterModel( scratch );
1119 
1120 	entity[0].flags |= RF_TRANSLUCENT|RF_SHELL_RED;
1121 	entity[0].alpha = 0.5 + 0.5 * sin(cls.realtime * 0.001f);
1122 
1123 	refdef.areabits = 0;
1124 	refdef.num_entities = 2;
1125 	refdef.entities = entity;
1126 	refdef.lightstyles = 0;
1127 	refdef.rdflags = RDF_NOWORLDMODEL;
1128 
1129 
1130 	ref.RenderFrame( &refdef );
1131 
1132 }
1133 
1134 
1135 void TestDraw( void ) {
1136 	color_t color = { 0, 0, 255, 33 };
1137 
1138 	ref.DrawFillEx( scr_vrect.x + 100, scr_vrect.y + 100, scr_vrect.width - 200, scr_vrect.height - 200, color );
1139 	PlayerConfig_MenuDraw();
1140 }
1141 #endif
1142 
1143 //=======================================================
1144 
1145 /*
1146 ==================
1147 SCR_DrawScreenFrame
1148 ==================
1149 */
SCR_DrawScreenFrame(float separation)1150 static void SCR_DrawScreenFrame( float separation ) {
1151 	ref.BeginFrame( separation );
1152 
1153 	/* if a cinematic is supposed to be running, handle menus
1154 	 * and console specially
1155 	 */
1156 	if( cl.cinematictime > 0 ) {
1157 		if( cls.key_dest & (KEY_CONSOLE|KEY_MENU) ) {
1158 			if( cl.cinematicpalette_active ) {
1159 				ref.CinematicSetPalette( NULL );
1160 				cl.cinematicpalette_active = qfalse;
1161 			}
1162 			UI_Draw( cls.realtime );
1163 			Con_DrawConsole();
1164 		} else {
1165 			SCR_DrawCinematic();
1166 		}
1167 		return;
1168 	}
1169 
1170 	/* make sure the game palette is active */
1171 	if( cl.cinematicpalette_active ) {
1172 		ref.CinematicSetPalette( NULL );
1173 		cl.cinematicpalette_active = qfalse;
1174 	}
1175 
1176 	switch( cls.state ) {
1177 	case ca_disconnected:
1178 		/* make sure at least fullscreen console or main menu is up */
1179 		if( !( cls.key_dest & (KEY_MENU|KEY_CONSOLE) ) ) {
1180 			Key_SetDest( cls.key_dest | KEY_CONSOLE );
1181 			Con_RunConsole();
1182 		}
1183 
1184 		/* draw main menu */
1185 		UI_Draw( cls.realtime );
1186 		break;
1187 
1188 	case ca_challenging:
1189 	case ca_connecting:
1190 	case ca_connected:
1191 	case ca_loading:
1192 	case ca_precached:
1193 		/* make sure main menu is down */
1194 		if( cls.key_dest & KEY_MENU ) {
1195 			UI_OpenMenu( UIMENU_NONE );
1196 		}
1197 
1198 		/* draw loading screen */
1199 		UI_DrawLoading( cls.realtime );
1200 		break;
1201 
1202 	case ca_active:
1203 		if( UI_IsTransparent() ) {
1204 			/* do 3D refresh drawing */
1205 			SCR_DrawActiveFrame( separation );
1206 		}
1207 
1208 		/* draw ingame menu */
1209 		UI_Draw( cls.realtime );
1210 		break;
1211 
1212 	default:
1213 		Com_Error( ERR_FATAL, "SCR_DrawScreenFrame: bad cls.state" );
1214 		break;
1215 	}
1216 
1217 	Con_DrawConsole();
1218 
1219 //	TestDraw();
1220 
1221 	if( scr_timegraph->integer )
1222 		SCR_DebugGraph( cls.frametime*300, 0 );
1223 
1224 	if( scr_debuggraph->integer || scr_timegraph->integer ||
1225             scr_netgraph->integer )
1226     {
1227 		SCR_DrawDebugGraph();
1228     }
1229 }
1230 
1231 /*
1232 ==================
1233 SCR_UpdateScreen
1234 
1235 This is called every frame, and can also be called explicitly to flush
1236 text to the screen.
1237 ==================
1238 */
SCR_UpdateScreen(void)1239 void SCR_UpdateScreen( void ) {
1240     static int recursive;
1241 
1242 	if( !scr_initialized )
1243 		return;				// not initialized yet
1244 
1245     if( recursive > 1 ) {
1246         Com_Error( ERR_FATAL, "SCR_UpdateScreen: recursively called" );
1247     }
1248 
1249     recursive++;
1250 
1251 	/*
1252 	** range check cl_camera_separation so we don't inadvertently fry someone's
1253 	** brain
1254 	*/
1255 	if( cl_stereo_separation->value > 1 )
1256 		Cvar_SetValue( "cl_stereo_separation", 1 );
1257 	else if( cl_stereo_separation->value < 0 )
1258 		Cvar_SetValue( "cl_stereo_separation", 0 );
1259 
1260 	if( cl_stereo->integer ) {
1261 		SCR_DrawScreenFrame( -cl_stereo_separation->value / 2 );
1262 		SCR_DrawScreenFrame( cl_stereo_separation->value / 2 );
1263 	} else {
1264 		SCR_DrawScreenFrame( 0 );
1265 	}
1266 
1267 	ref.EndFrame();
1268 
1269     recursive--;
1270 }
1271 
1272 
1273