Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

cl_scrn.c (27519B)


      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 /*
     23 
     24   full screen console
     25   put up loading plaque
     26   blanked background with loading plaque
     27   blanked background with menu
     28   cinematics
     29   full screen image for quit and victory
     30 
     31   end of unit intermissions
     32 
     33   */
     34 
     35 #include "client.h"
     36 
     37 float		scr_con_current;	// aproaches scr_conlines at scr_conspeed
     38 float		scr_conlines;		// 0.0 to 1.0 lines of console to display
     39 
     40 qboolean	scr_initialized;		// ready to draw
     41 
     42 int			scr_draw_loading;
     43 
     44 vrect_t		scr_vrect;		// position of render window on screen
     45 
     46 
     47 cvar_t		*scr_viewsize;
     48 cvar_t		*scr_conspeed;
     49 cvar_t		*scr_centertime;
     50 cvar_t		*scr_showturtle;
     51 cvar_t		*scr_showpause;
     52 cvar_t		*scr_printspeed;
     53 
     54 cvar_t		*scr_netgraph;
     55 cvar_t		*scr_timegraph;
     56 cvar_t		*scr_debuggraph;
     57 cvar_t		*scr_graphheight;
     58 cvar_t		*scr_graphscale;
     59 cvar_t		*scr_graphshift;
     60 cvar_t		*scr_drawall;
     61 
     62 typedef struct
     63 {
     64 	int		x1, y1, x2, y2;
     65 } dirty_t;
     66 
     67 dirty_t		scr_dirty, scr_old_dirty[2];
     68 
     69 char		crosshair_pic[MAX_QPATH];
     70 int			crosshair_width, crosshair_height;
     71 
     72 void SCR_TimeRefresh_f (void);
     73 void SCR_Loading_f (void);
     74 
     75 
     76 /*
     77 ===============================================================================
     78 
     79 BAR GRAPHS
     80 
     81 ===============================================================================
     82 */
     83 
     84 /*
     85 ==============
     86 CL_AddNetgraph
     87 
     88 A new packet was just parsed
     89 ==============
     90 */
     91 void CL_AddNetgraph (void)
     92 {
     93 	int		i;
     94 	int		in;
     95 	int		ping;
     96 
     97 	// if using the debuggraph for something else, don't
     98 	// add the net lines
     99 	if (scr_debuggraph->value || scr_timegraph->value)
    100 		return;
    101 
    102 	for (i=0 ; i<cls.netchan.dropped ; i++)
    103 		SCR_DebugGraph (30, 0x40);
    104 
    105 	for (i=0 ; i<cl.surpressCount ; i++)
    106 		SCR_DebugGraph (30, 0xdf);
    107 
    108 	// see what the latency was on this packet
    109 	in = cls.netchan.incoming_acknowledged & (CMD_BACKUP-1);
    110 	ping = cls.realtime - cl.cmd_time[in];
    111 	ping /= 30;
    112 	if (ping > 30)
    113 		ping = 30;
    114 	SCR_DebugGraph (ping, 0xd0);
    115 }
    116 
    117 
    118 typedef struct
    119 {
    120 	float	value;
    121 	int		color;
    122 } graphsamp_t;
    123 
    124 static	int			current;
    125 static	graphsamp_t	values[1024];
    126 
    127 /*
    128 ==============
    129 SCR_DebugGraph
    130 ==============
    131 */
    132 void SCR_DebugGraph (float value, int color)
    133 {
    134 	values[current&1023].value = value;
    135 	values[current&1023].color = color;
    136 	current++;
    137 }
    138 
    139 /*
    140 ==============
    141 SCR_DrawDebugGraph
    142 ==============
    143 */
    144 void SCR_DrawDebugGraph (void)
    145 {
    146 	int		a, x, y, w, i, h;
    147 	float	v;
    148 	int		color;
    149 
    150 	//
    151 	// draw the graph
    152 	//
    153 	w = scr_vrect.width;
    154 
    155 	x = scr_vrect.x;
    156 	y = scr_vrect.y+scr_vrect.height;
    157 	re.DrawFill (x, y-scr_graphheight->value,
    158 		w, scr_graphheight->value, 8);
    159 
    160 	for (a=0 ; a<w ; a++)
    161 	{
    162 		i = (current-1-a+1024) & 1023;
    163 		v = values[i].value;
    164 		color = values[i].color;
    165 		v = v*scr_graphscale->value + scr_graphshift->value;
    166 		
    167 		if (v < 0)
    168 			v += scr_graphheight->value * (1+(int)(-v/scr_graphheight->value));
    169 		h = (int)v % (int)scr_graphheight->value;
    170 		re.DrawFill (x+w-1-a, y - h, 1,	h, color);
    171 	}
    172 }
    173 
    174 /*
    175 ===============================================================================
    176 
    177 CENTER PRINTING
    178 
    179 ===============================================================================
    180 */
    181 
    182 char		scr_centerstring[1024];
    183 float		scr_centertime_start;	// for slow victory printing
    184 float		scr_centertime_off;
    185 int			scr_center_lines;
    186 int			scr_erase_center;
    187 
    188 /*
    189 ==============
    190 SCR_CenterPrint
    191 
    192 Called for important messages that should stay in the center of the screen
    193 for a few moments
    194 ==============
    195 */
    196 void SCR_CenterPrint (char *str)
    197 {
    198 	char	*s;
    199 	char	line[64];
    200 	int		i, j, l;
    201 
    202 	strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
    203 	scr_centertime_off = scr_centertime->value;
    204 	scr_centertime_start = cl.time;
    205 
    206 	// count the number of lines for centering
    207 	scr_center_lines = 1;
    208 	s = str;
    209 	while (*s)
    210 	{
    211 		if (*s == '\n')
    212 			scr_center_lines++;
    213 		s++;
    214 	}
    215 
    216 	// echo it to the console
    217 	Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
    218 
    219 	s = str;
    220 	do	
    221 	{
    222 	// scan the width of the line
    223 		for (l=0 ; l<40 ; l++)
    224 			if (s[l] == '\n' || !s[l])
    225 				break;
    226 		for (i=0 ; i<(40-l)/2 ; i++)
    227 			line[i] = ' ';
    228 
    229 		for (j=0 ; j<l ; j++)
    230 		{
    231 			line[i++] = s[j];
    232 		}
    233 
    234 		line[i] = '\n';
    235 		line[i+1] = 0;
    236 
    237 		Com_Printf ("%s", line);
    238 
    239 		while (*s && *s != '\n')
    240 			s++;
    241 
    242 		if (!*s)
    243 			break;
    244 		s++;		// skip the \n
    245 	} while (1);
    246 	Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
    247 	Con_ClearNotify ();
    248 }
    249 
    250 
    251 void SCR_DrawCenterString (void)
    252 {
    253 	char	*start;
    254 	int		l;
    255 	int		j;
    256 	int		x, y;
    257 	int		remaining;
    258 
    259 // the finale prints the characters one at a time
    260 	remaining = 9999;
    261 
    262 	scr_erase_center = 0;
    263 	start = scr_centerstring;
    264 
    265 	if (scr_center_lines <= 4)
    266 		y = viddef.height*0.35;
    267 	else
    268 		y = 48;
    269 
    270 	do	
    271 	{
    272 	// scan the width of the line
    273 		for (l=0 ; l<40 ; l++)
    274 			if (start[l] == '\n' || !start[l])
    275 				break;
    276 		x = (viddef.width - l*8)/2;
    277 		SCR_AddDirtyPoint (x, y);
    278 		for (j=0 ; j<l ; j++, x+=8)
    279 		{
    280 			re.DrawChar (x, y, start[j]);	
    281 			if (!remaining--)
    282 				return;
    283 		}
    284 		SCR_AddDirtyPoint (x, y+8);
    285 			
    286 		y += 8;
    287 
    288 		while (*start && *start != '\n')
    289 			start++;
    290 
    291 		if (!*start)
    292 			break;
    293 		start++;		// skip the \n
    294 	} while (1);
    295 }
    296 
    297 void SCR_CheckDrawCenterString (void)
    298 {
    299 	scr_centertime_off -= cls.frametime;
    300 	
    301 	if (scr_centertime_off <= 0)
    302 		return;
    303 
    304 	SCR_DrawCenterString ();
    305 }
    306 
    307 //=============================================================================
    308 
    309 /*
    310 =================
    311 SCR_CalcVrect
    312 
    313 Sets scr_vrect, the coordinates of the rendered window
    314 =================
    315 */
    316 static void SCR_CalcVrect (void)
    317 {
    318 	int		size;
    319 
    320 	// bound viewsize
    321 	if (scr_viewsize->value < 40)
    322 		Cvar_Set ("viewsize","40");
    323 	if (scr_viewsize->value > 100)
    324 		Cvar_Set ("viewsize","100");
    325 
    326 	size = scr_viewsize->value;
    327 
    328 	scr_vrect.width = viddef.width*size/100;
    329 	scr_vrect.width &= ~7;
    330 
    331 	scr_vrect.height = viddef.height*size/100;
    332 	scr_vrect.height &= ~1;
    333 
    334 	scr_vrect.x = (viddef.width - scr_vrect.width)/2;
    335 	scr_vrect.y = (viddef.height - scr_vrect.height)/2;
    336 }
    337 
    338 
    339 /*
    340 =================
    341 SCR_SizeUp_f
    342 
    343 Keybinding command
    344 =================
    345 */
    346 void SCR_SizeUp_f (void)
    347 {
    348 	Cvar_SetValue ("viewsize",scr_viewsize->value+10);
    349 }
    350 
    351 
    352 /*
    353 =================
    354 SCR_SizeDown_f
    355 
    356 Keybinding command
    357 =================
    358 */
    359 void SCR_SizeDown_f (void)
    360 {
    361 	Cvar_SetValue ("viewsize",scr_viewsize->value-10);
    362 }
    363 
    364 /*
    365 =================
    366 SCR_Sky_f
    367 
    368 Set a specific sky and rotation speed
    369 =================
    370 */
    371 void SCR_Sky_f (void)
    372 {
    373 	float	rotate;
    374 	vec3_t	axis;
    375 
    376 	if (Cmd_Argc() < 2)
    377 	{
    378 		Com_Printf ("Usage: sky <basename> <rotate> <axis x y z>\n");
    379 		return;
    380 	}
    381 	if (Cmd_Argc() > 2)
    382 		rotate = atof(Cmd_Argv(2));
    383 	else
    384 		rotate = 0;
    385 	if (Cmd_Argc() == 6)
    386 	{
    387 		axis[0] = atof(Cmd_Argv(3));
    388 		axis[1] = atof(Cmd_Argv(4));
    389 		axis[2] = atof(Cmd_Argv(5));
    390 	}
    391 	else
    392 	{
    393 		axis[0] = 0;
    394 		axis[1] = 0;
    395 		axis[2] = 1;
    396 	}
    397 
    398 	re.SetSky (Cmd_Argv(1), rotate, axis);
    399 }
    400 
    401 //============================================================================
    402 
    403 /*
    404 ==================
    405 SCR_Init
    406 ==================
    407 */
    408 void SCR_Init (void)
    409 {
    410 	scr_viewsize = Cvar_Get ("viewsize", "100", CVAR_ARCHIVE);
    411 	scr_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
    412 	scr_showturtle = Cvar_Get ("scr_showturtle", "0", 0);
    413 	scr_showpause = Cvar_Get ("scr_showpause", "1", 0);
    414 	scr_centertime = Cvar_Get ("scr_centertime", "2.5", 0);
    415 	scr_printspeed = Cvar_Get ("scr_printspeed", "8", 0);
    416 	scr_netgraph = Cvar_Get ("netgraph", "0", 0);
    417 	scr_timegraph = Cvar_Get ("timegraph", "0", 0);
    418 	scr_debuggraph = Cvar_Get ("debuggraph", "0", 0);
    419 	scr_graphheight = Cvar_Get ("graphheight", "32", 0);
    420 	scr_graphscale = Cvar_Get ("graphscale", "1", 0);
    421 	scr_graphshift = Cvar_Get ("graphshift", "0", 0);
    422 	scr_drawall = Cvar_Get ("scr_drawall", "0", 0);
    423 
    424 //
    425 // register our commands
    426 //
    427 	Cmd_AddCommand ("timerefresh",SCR_TimeRefresh_f);
    428 	Cmd_AddCommand ("loading",SCR_Loading_f);
    429 	Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
    430 	Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
    431 	Cmd_AddCommand ("sky",SCR_Sky_f);
    432 
    433 	scr_initialized = true;
    434 }
    435 
    436 
    437 /*
    438 ==============
    439 SCR_DrawNet
    440 ==============
    441 */
    442 void SCR_DrawNet (void)
    443 {
    444 	if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged 
    445 		< CMD_BACKUP-1)
    446 		return;
    447 
    448 	re.DrawPic (scr_vrect.x+64, scr_vrect.y, "net");
    449 }
    450 
    451 /*
    452 ==============
    453 SCR_DrawPause
    454 ==============
    455 */
    456 void SCR_DrawPause (void)
    457 {
    458 	int		w, h;
    459 
    460 	if (!scr_showpause->value)		// turn off for screenshots
    461 		return;
    462 
    463 	if (!cl_paused->value)
    464 		return;
    465 
    466 	re.DrawGetPicSize (&w, &h, "pause");
    467 	re.DrawPic ((viddef.width-w)/2, viddef.height/2 + 8, "pause");
    468 }
    469 
    470 /*
    471 ==============
    472 SCR_DrawLoading
    473 ==============
    474 */
    475 void SCR_DrawLoading (void)
    476 {
    477 	int		w, h;
    478 		
    479 	if (!scr_draw_loading)
    480 		return;
    481 
    482 	scr_draw_loading = false;
    483 	re.DrawGetPicSize (&w, &h, "loading");
    484 	re.DrawPic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
    485 }
    486 
    487 //=============================================================================
    488 
    489 /*
    490 ==================
    491 SCR_RunConsole
    492 
    493 Scroll it up or down
    494 ==================
    495 */
    496 void SCR_RunConsole (void)
    497 {
    498 // decide on the height of the console
    499 	if (cls.key_dest == key_console)
    500 		scr_conlines = 0.5;		// half screen
    501 	else
    502 		scr_conlines = 0;				// none visible
    503 	
    504 	if (scr_conlines < scr_con_current)
    505 	{
    506 		scr_con_current -= scr_conspeed->value*cls.frametime;
    507 		if (scr_conlines > scr_con_current)
    508 			scr_con_current = scr_conlines;
    509 
    510 	}
    511 	else if (scr_conlines > scr_con_current)
    512 	{
    513 		scr_con_current += scr_conspeed->value*cls.frametime;
    514 		if (scr_conlines < scr_con_current)
    515 			scr_con_current = scr_conlines;
    516 	}
    517 
    518 }
    519 
    520 /*
    521 ==================
    522 SCR_DrawConsole
    523 ==================
    524 */
    525 void SCR_DrawConsole (void)
    526 {
    527 	Con_CheckResize ();
    528 	
    529 	if (cls.state == ca_disconnected || cls.state == ca_connecting)
    530 	{	// forced full screen console
    531 		Con_DrawConsole (1.0);
    532 		return;
    533 	}
    534 
    535 	if (cls.state != ca_active || !cl.refresh_prepped)
    536 	{	// connected, but can't render
    537 		Con_DrawConsole (0.5);
    538 		re.DrawFill (0, viddef.height/2, viddef.width, viddef.height/2, 0);
    539 		return;
    540 	}
    541 
    542 	if (scr_con_current)
    543 	{
    544 		Con_DrawConsole (scr_con_current);
    545 	}
    546 	else
    547 	{
    548 		if (cls.key_dest == key_game || cls.key_dest == key_message)
    549 			Con_DrawNotify ();	// only draw notify in game
    550 	}
    551 }
    552 
    553 //=============================================================================
    554 
    555 /*
    556 ================
    557 SCR_BeginLoadingPlaque
    558 ================
    559 */
    560 void SCR_BeginLoadingPlaque (void)
    561 {
    562 	S_StopAllSounds ();
    563 	cl.sound_prepped = false;		// don't play ambients
    564 	CDAudio_Stop ();
    565 	if (cls.disable_screen)
    566 		return;
    567 	if (developer->value)
    568 		return;
    569 	if (cls.state == ca_disconnected)
    570 		return;	// if at console, don't bring up the plaque
    571 	if (cls.key_dest == key_console)
    572 		return;
    573 	if (cl.cinematictime > 0)
    574 		scr_draw_loading = 2;	// clear to balack first
    575 	else
    576 		scr_draw_loading = 1;
    577 	SCR_UpdateScreen ();
    578 	cls.disable_screen = Sys_Milliseconds ();
    579 	cls.disable_servercount = cl.servercount;
    580 }
    581 
    582 /*
    583 ================
    584 SCR_EndLoadingPlaque
    585 ================
    586 */
    587 void SCR_EndLoadingPlaque (void)
    588 {
    589 	cls.disable_screen = 0;
    590 	Con_ClearNotify ();
    591 }
    592 
    593 /*
    594 ================
    595 SCR_Loading_f
    596 ================
    597 */
    598 void SCR_Loading_f (void)
    599 {
    600 	SCR_BeginLoadingPlaque ();
    601 }
    602 
    603 /*
    604 ================
    605 SCR_TimeRefresh_f
    606 ================
    607 */
    608 int entitycmpfnc( const entity_t *a, const entity_t *b )
    609 {
    610 	/*
    611 	** all other models are sorted by model then skin
    612 	*/
    613 	if ( a->model == b->model )
    614 	{
    615 		return ( ( int ) a->skin - ( int ) b->skin );
    616 	}
    617 	else
    618 	{
    619 		return ( ( int ) a->model - ( int ) b->model );
    620 	}
    621 }
    622 
    623 void SCR_TimeRefresh_f (void)
    624 {
    625 	int		i;
    626 	int		start, stop;
    627 	float	time;
    628 
    629 	if ( cls.state != ca_active )
    630 		return;
    631 
    632 	start = Sys_Milliseconds ();
    633 
    634 	if (Cmd_Argc() == 2)
    635 	{	// run without page flipping
    636 		re.BeginFrame( 0 );
    637 		for (i=0 ; i<128 ; i++)
    638 		{
    639 			cl.refdef.viewangles[1] = i/128.0*360.0;
    640 			re.RenderFrame (&cl.refdef);
    641 		}
    642 		re.EndFrame();
    643 	}
    644 	else
    645 	{
    646 		for (i=0 ; i<128 ; i++)
    647 		{
    648 			cl.refdef.viewangles[1] = i/128.0*360.0;
    649 
    650 			re.BeginFrame( 0 );
    651 			re.RenderFrame (&cl.refdef);
    652 			re.EndFrame();
    653 		}
    654 	}
    655 
    656 	stop = Sys_Milliseconds ();
    657 	time = (stop-start)/1000.0;
    658 	Com_Printf ("%f seconds (%f fps)\n", time, 128/time);
    659 }
    660 
    661 /*
    662 =================
    663 SCR_AddDirtyPoint
    664 =================
    665 */
    666 void SCR_AddDirtyPoint (int x, int y)
    667 {
    668 	if (x < scr_dirty.x1)
    669 		scr_dirty.x1 = x;
    670 	if (x > scr_dirty.x2)
    671 		scr_dirty.x2 = x;
    672 	if (y < scr_dirty.y1)
    673 		scr_dirty.y1 = y;
    674 	if (y > scr_dirty.y2)
    675 		scr_dirty.y2 = y;
    676 }
    677 
    678 void SCR_DirtyScreen (void)
    679 {
    680 	SCR_AddDirtyPoint (0, 0);
    681 	SCR_AddDirtyPoint (viddef.width-1, viddef.height-1);
    682 }
    683 
    684 /*
    685 ==============
    686 SCR_TileClear
    687 
    688 Clear any parts of the tiled background that were drawn on last frame
    689 ==============
    690 */
    691 void SCR_TileClear (void)
    692 {
    693 	int		i;
    694 	int		top, bottom, left, right;
    695 	dirty_t	clear;
    696 
    697 	if (scr_drawall->value)
    698 		SCR_DirtyScreen ();	// for power vr or broken page flippers...
    699 
    700 	if (scr_con_current == 1.0)
    701 		return;		// full screen console
    702 	if (scr_viewsize->value == 100)
    703 		return;		// full screen rendering
    704 	if (cl.cinematictime > 0)
    705 		return;		// full screen cinematic
    706 
    707 	// erase rect will be the union of the past three frames
    708 	// so tripple buffering works properly
    709 	clear = scr_dirty;
    710 	for (i=0 ; i<2 ; i++)
    711 	{
    712 		if (scr_old_dirty[i].x1 < clear.x1)
    713 			clear.x1 = scr_old_dirty[i].x1;
    714 		if (scr_old_dirty[i].x2 > clear.x2)
    715 			clear.x2 = scr_old_dirty[i].x2;
    716 		if (scr_old_dirty[i].y1 < clear.y1)
    717 			clear.y1 = scr_old_dirty[i].y1;
    718 		if (scr_old_dirty[i].y2 > clear.y2)
    719 			clear.y2 = scr_old_dirty[i].y2;
    720 	}
    721 
    722 	scr_old_dirty[1] = scr_old_dirty[0];
    723 	scr_old_dirty[0] = scr_dirty;
    724 
    725 	scr_dirty.x1 = 9999;
    726 	scr_dirty.x2 = -9999;
    727 	scr_dirty.y1 = 9999;
    728 	scr_dirty.y2 = -9999;
    729 
    730 	// don't bother with anything convered by the console)
    731 	top = scr_con_current*viddef.height;
    732 	if (top >= clear.y1)
    733 		clear.y1 = top;
    734 
    735 	if (clear.y2 <= clear.y1)
    736 		return;		// nothing disturbed
    737 
    738 	top = scr_vrect.y;
    739 	bottom = top + scr_vrect.height-1;
    740 	left = scr_vrect.x;
    741 	right = left + scr_vrect.width-1;
    742 
    743 	if (clear.y1 < top)
    744 	{	// clear above view screen
    745 		i = clear.y2 < top-1 ? clear.y2 : top-1;
    746 		re.DrawTileClear (clear.x1 , clear.y1,
    747 			clear.x2 - clear.x1 + 1, i - clear.y1+1, "backtile");
    748 		clear.y1 = top;
    749 	}
    750 	if (clear.y2 > bottom)
    751 	{	// clear below view screen
    752 		i = clear.y1 > bottom+1 ? clear.y1 : bottom+1;
    753 		re.DrawTileClear (clear.x1, i,
    754 			clear.x2-clear.x1+1, clear.y2-i+1, "backtile");
    755 		clear.y2 = bottom;
    756 	}
    757 	if (clear.x1 < left)
    758 	{	// clear left of view screen
    759 		i = clear.x2 < left-1 ? clear.x2 : left-1;
    760 		re.DrawTileClear (clear.x1, clear.y1,
    761 			i-clear.x1+1, clear.y2 - clear.y1 + 1, "backtile");
    762 		clear.x1 = left;
    763 	}
    764 	if (clear.x2 > right)
    765 	{	// clear left of view screen
    766 		i = clear.x1 > right+1 ? clear.x1 : right+1;
    767 		re.DrawTileClear (i, clear.y1,
    768 			clear.x2-i+1, clear.y2 - clear.y1 + 1, "backtile");
    769 		clear.x2 = right;
    770 	}
    771 
    772 }
    773 
    774 
    775 //===============================================================
    776 
    777 
    778 #define STAT_MINUS		10	// num frame for '-' stats digit
    779 char		*sb_nums[2][11] = 
    780 {
    781 	{"num_0", "num_1", "num_2", "num_3", "num_4", "num_5",
    782 	"num_6", "num_7", "num_8", "num_9", "num_minus"},
    783 	{"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
    784 	"anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}
    785 };
    786 
    787 #define	ICON_WIDTH	24
    788 #define	ICON_HEIGHT	24
    789 #define	CHAR_WIDTH	16
    790 #define	ICON_SPACE	8
    791 
    792 
    793 
    794 /*
    795 ================
    796 SizeHUDString
    797 
    798 Allow embedded \n in the string
    799 ================
    800 */
    801 void SizeHUDString (char *string, int *w, int *h)
    802 {
    803 	int		lines, width, current;
    804 
    805 	lines = 1;
    806 	width = 0;
    807 
    808 	current = 0;
    809 	while (*string)
    810 	{
    811 		if (*string == '\n')
    812 		{
    813 			lines++;
    814 			current = 0;
    815 		}
    816 		else
    817 		{
    818 			current++;
    819 			if (current > width)
    820 				width = current;
    821 		}
    822 		string++;
    823 	}
    824 
    825 	*w = width * 8;
    826 	*h = lines * 8;
    827 }
    828 
    829 void DrawHUDString (char *string, int x, int y, int centerwidth, int xor)
    830 {
    831 	int		margin;
    832 	char	line[1024];
    833 	int		width;
    834 	int		i;
    835 
    836 	margin = x;
    837 
    838 	while (*string)
    839 	{
    840 		// scan out one line of text from the string
    841 		width = 0;
    842 		while (*string && *string != '\n')
    843 			line[width++] = *string++;
    844 		line[width] = 0;
    845 
    846 		if (centerwidth)
    847 			x = margin + (centerwidth - width*8)/2;
    848 		else
    849 			x = margin;
    850 		for (i=0 ; i<width ; i++)
    851 		{
    852 			re.DrawChar (x, y, line[i]^xor);
    853 			x += 8;
    854 		}
    855 		if (*string)
    856 		{
    857 			string++;	// skip the \n
    858 			x = margin;
    859 			y += 8;
    860 		}
    861 	}
    862 }
    863 
    864 
    865 /*
    866 ==============
    867 SCR_DrawField
    868 ==============
    869 */
    870 void SCR_DrawField (int x, int y, int color, int width, int value)
    871 {
    872 	char	num[16], *ptr;
    873 	int		l;
    874 	int		frame;
    875 
    876 	if (width < 1)
    877 		return;
    878 
    879 	// draw number string
    880 	if (width > 5)
    881 		width = 5;
    882 
    883 	SCR_AddDirtyPoint (x, y);
    884 	SCR_AddDirtyPoint (x+width*CHAR_WIDTH+2, y+23);
    885 
    886 	Com_sprintf (num, sizeof(num), "%i", value);
    887 	l = strlen(num);
    888 	if (l > width)
    889 		l = width;
    890 	x += 2 + CHAR_WIDTH*(width - l);
    891 
    892 	ptr = num;
    893 	while (*ptr && l)
    894 	{
    895 		if (*ptr == '-')
    896 			frame = STAT_MINUS;
    897 		else
    898 			frame = *ptr -'0';
    899 
    900 		re.DrawPic (x,y,sb_nums[color][frame]);
    901 		x += CHAR_WIDTH;
    902 		ptr++;
    903 		l--;
    904 	}
    905 }
    906 
    907 
    908 /*
    909 ===============
    910 SCR_TouchPics
    911 
    912 Allows rendering code to cache all needed sbar graphics
    913 ===============
    914 */
    915 void SCR_TouchPics (void)
    916 {
    917 	int		i, j;
    918 
    919 	for (i=0 ; i<2 ; i++)
    920 		for (j=0 ; j<11 ; j++)
    921 			re.RegisterPic (sb_nums[i][j]);
    922 
    923 	if (crosshair->value)
    924 	{
    925 		if (crosshair->value > 3 || crosshair->value < 0)
    926 			crosshair->value = 3;
    927 
    928 		Com_sprintf (crosshair_pic, sizeof(crosshair_pic), "ch%i", (int)(crosshair->value));
    929 		re.DrawGetPicSize (&crosshair_width, &crosshair_height, crosshair_pic);
    930 		if (!crosshair_width)
    931 			crosshair_pic[0] = 0;
    932 	}
    933 }
    934 
    935 /*
    936 ================
    937 SCR_ExecuteLayoutString 
    938 
    939 ================
    940 */
    941 void SCR_ExecuteLayoutString (char *s)
    942 {
    943 	int		x, y;
    944 	int		value;
    945 	char	*token;
    946 	int		width;
    947 	int		index;
    948 	clientinfo_t	*ci;
    949 
    950 	if (cls.state != ca_active || !cl.refresh_prepped)
    951 		return;
    952 
    953 	if (!s[0])
    954 		return;
    955 
    956 	x = 0;
    957 	y = 0;
    958 	width = 3;
    959 
    960 	while (s)
    961 	{
    962 		token = COM_Parse (&s);
    963 		if (!strcmp(token, "xl"))
    964 		{
    965 			token = COM_Parse (&s);
    966 			x = atoi(token);
    967 			continue;
    968 		}
    969 		if (!strcmp(token, "xr"))
    970 		{
    971 			token = COM_Parse (&s);
    972 			x = viddef.width + atoi(token);
    973 			continue;
    974 		}
    975 		if (!strcmp(token, "xv"))
    976 		{
    977 			token = COM_Parse (&s);
    978 			x = viddef.width/2 - 160 + atoi(token);
    979 			continue;
    980 		}
    981 
    982 		if (!strcmp(token, "yt"))
    983 		{
    984 			token = COM_Parse (&s);
    985 			y = atoi(token);
    986 			continue;
    987 		}
    988 		if (!strcmp(token, "yb"))
    989 		{
    990 			token = COM_Parse (&s);
    991 			y = viddef.height + atoi(token);
    992 			continue;
    993 		}
    994 		if (!strcmp(token, "yv"))
    995 		{
    996 			token = COM_Parse (&s);
    997 			y = viddef.height/2 - 120 + atoi(token);
    998 			continue;
    999 		}
   1000 
   1001 		if (!strcmp(token, "pic"))
   1002 		{	// draw a pic from a stat number
   1003 			token = COM_Parse (&s);
   1004 			value = cl.frame.playerstate.stats[atoi(token)];
   1005 			if (value >= MAX_IMAGES)
   1006 				Com_Error (ERR_DROP, "Pic >= MAX_IMAGES");
   1007 			if (cl.configstrings[CS_IMAGES+value])
   1008 			{
   1009 				SCR_AddDirtyPoint (x, y);
   1010 				SCR_AddDirtyPoint (x+23, y+23);
   1011 				re.DrawPic (x, y, cl.configstrings[CS_IMAGES+value]);
   1012 			}
   1013 			continue;
   1014 		}
   1015 
   1016 		if (!strcmp(token, "client"))
   1017 		{	// draw a deathmatch client block
   1018 			int		score, ping, time;
   1019 
   1020 			token = COM_Parse (&s);
   1021 			x = viddef.width/2 - 160 + atoi(token);
   1022 			token = COM_Parse (&s);
   1023 			y = viddef.height/2 - 120 + atoi(token);
   1024 			SCR_AddDirtyPoint (x, y);
   1025 			SCR_AddDirtyPoint (x+159, y+31);
   1026 
   1027 			token = COM_Parse (&s);
   1028 			value = atoi(token);
   1029 			if (value >= MAX_CLIENTS || value < 0)
   1030 				Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
   1031 			ci = &cl.clientinfo[value];
   1032 
   1033 			token = COM_Parse (&s);
   1034 			score = atoi(token);
   1035 
   1036 			token = COM_Parse (&s);
   1037 			ping = atoi(token);
   1038 
   1039 			token = COM_Parse (&s);
   1040 			time = atoi(token);
   1041 
   1042 			DrawAltString (x+32, y, ci->name);
   1043 			DrawString (x+32, y+8,  "Score: ");
   1044 			DrawAltString (x+32+7*8, y+8,  va("%i", score));
   1045 			DrawString (x+32, y+16, va("Ping:  %i", ping));
   1046 			DrawString (x+32, y+24, va("Time:  %i", time));
   1047 
   1048 			if (!ci->icon)
   1049 				ci = &cl.baseclientinfo;
   1050 			re.DrawPic (x, y, ci->iconname);
   1051 			continue;
   1052 		}
   1053 
   1054 		if (!strcmp(token, "ctf"))
   1055 		{	// draw a ctf client block
   1056 			int		score, ping;
   1057 			char	block[80];
   1058 
   1059 			token = COM_Parse (&s);
   1060 			x = viddef.width/2 - 160 + atoi(token);
   1061 			token = COM_Parse (&s);
   1062 			y = viddef.height/2 - 120 + atoi(token);
   1063 			SCR_AddDirtyPoint (x, y);
   1064 			SCR_AddDirtyPoint (x+159, y+31);
   1065 
   1066 			token = COM_Parse (&s);
   1067 			value = atoi(token);
   1068 			if (value >= MAX_CLIENTS || value < 0)
   1069 				Com_Error (ERR_DROP, "client >= MAX_CLIENTS");
   1070 			ci = &cl.clientinfo[value];
   1071 
   1072 			token = COM_Parse (&s);
   1073 			score = atoi(token);
   1074 
   1075 			token = COM_Parse (&s);
   1076 			ping = atoi(token);
   1077 			if (ping > 999)
   1078 				ping = 999;
   1079 
   1080 			sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name);
   1081 
   1082 			if (value == cl.playernum)
   1083 				DrawAltString (x, y, block);
   1084 			else
   1085 				DrawString (x, y, block);
   1086 			continue;
   1087 		}
   1088 
   1089 		if (!strcmp(token, "picn"))
   1090 		{	// draw a pic from a name
   1091 			token = COM_Parse (&s);
   1092 			SCR_AddDirtyPoint (x, y);
   1093 			SCR_AddDirtyPoint (x+23, y+23);
   1094 			re.DrawPic (x, y, token);
   1095 			continue;
   1096 		}
   1097 
   1098 		if (!strcmp(token, "num"))
   1099 		{	// draw a number
   1100 			token = COM_Parse (&s);
   1101 			width = atoi(token);
   1102 			token = COM_Parse (&s);
   1103 			value = cl.frame.playerstate.stats[atoi(token)];
   1104 			SCR_DrawField (x, y, 0, width, value);
   1105 			continue;
   1106 		}
   1107 
   1108 		if (!strcmp(token, "hnum"))
   1109 		{	// health number
   1110 			int		color;
   1111 
   1112 			width = 3;
   1113 			value = cl.frame.playerstate.stats[STAT_HEALTH];
   1114 			if (value > 25)
   1115 				color = 0;	// green
   1116 			else if (value > 0)
   1117 				color = (cl.frame.serverframe>>2) & 1;		// flash
   1118 			else
   1119 				color = 1;
   1120 
   1121 			if (cl.frame.playerstate.stats[STAT_FLASHES] & 1)
   1122 				re.DrawPic (x, y, "field_3");
   1123 
   1124 			SCR_DrawField (x, y, color, width, value);
   1125 			continue;
   1126 		}
   1127 
   1128 		if (!strcmp(token, "anum"))
   1129 		{	// ammo number
   1130 			int		color;
   1131 
   1132 			width = 3;
   1133 			value = cl.frame.playerstate.stats[STAT_AMMO];
   1134 			if (value > 5)
   1135 				color = 0;	// green
   1136 			else if (value >= 0)
   1137 				color = (cl.frame.serverframe>>2) & 1;		// flash
   1138 			else
   1139 				continue;	// negative number = don't show
   1140 
   1141 			if (cl.frame.playerstate.stats[STAT_FLASHES] & 4)
   1142 				re.DrawPic (x, y, "field_3");
   1143 
   1144 			SCR_DrawField (x, y, color, width, value);
   1145 			continue;
   1146 		}
   1147 
   1148 		if (!strcmp(token, "rnum"))
   1149 		{	// armor number
   1150 			int		color;
   1151 
   1152 			width = 3;
   1153 			value = cl.frame.playerstate.stats[STAT_ARMOR];
   1154 			if (value < 1)
   1155 				continue;
   1156 
   1157 			color = 0;	// green
   1158 
   1159 			if (cl.frame.playerstate.stats[STAT_FLASHES] & 2)
   1160 				re.DrawPic (x, y, "field_3");
   1161 
   1162 			SCR_DrawField (x, y, color, width, value);
   1163 			continue;
   1164 		}
   1165 
   1166 
   1167 		if (!strcmp(token, "stat_string"))
   1168 		{
   1169 			token = COM_Parse (&s);
   1170 			index = atoi(token);
   1171 			if (index < 0 || index >= MAX_CONFIGSTRINGS)
   1172 				Com_Error (ERR_DROP, "Bad stat_string index");
   1173 			index = cl.frame.playerstate.stats[index];
   1174 			if (index < 0 || index >= MAX_CONFIGSTRINGS)
   1175 				Com_Error (ERR_DROP, "Bad stat_string index");
   1176 			DrawString (x, y, cl.configstrings[index]);
   1177 			continue;
   1178 		}
   1179 
   1180 		if (!strcmp(token, "cstring"))
   1181 		{
   1182 			token = COM_Parse (&s);
   1183 			DrawHUDString (token, x, y, 320, 0);
   1184 			continue;
   1185 		}
   1186 
   1187 		if (!strcmp(token, "string"))
   1188 		{
   1189 			token = COM_Parse (&s);
   1190 			DrawString (x, y, token);
   1191 			continue;
   1192 		}
   1193 
   1194 		if (!strcmp(token, "cstring2"))
   1195 		{
   1196 			token = COM_Parse (&s);
   1197 			DrawHUDString (token, x, y, 320,0x80);
   1198 			continue;
   1199 		}
   1200 
   1201 		if (!strcmp(token, "string2"))
   1202 		{
   1203 			token = COM_Parse (&s);
   1204 			DrawAltString (x, y, token);
   1205 			continue;
   1206 		}
   1207 
   1208 		if (!strcmp(token, "if"))
   1209 		{	// draw a number
   1210 			token = COM_Parse (&s);
   1211 			value = cl.frame.playerstate.stats[atoi(token)];
   1212 			if (!value)
   1213 			{	// skip to endif
   1214 				while (s && strcmp(token, "endif") )
   1215 				{
   1216 					token = COM_Parse (&s);
   1217 				}
   1218 			}
   1219 
   1220 			continue;
   1221 		}
   1222 
   1223 
   1224 	}
   1225 }
   1226 
   1227 
   1228 /*
   1229 ================
   1230 SCR_DrawStats
   1231 
   1232 The status bar is a small layout program that
   1233 is based on the stats array
   1234 ================
   1235 */
   1236 void SCR_DrawStats (void)
   1237 {
   1238 	SCR_ExecuteLayoutString (cl.configstrings[CS_STATUSBAR]);
   1239 }
   1240 
   1241 
   1242 /*
   1243 ================
   1244 SCR_DrawLayout
   1245 
   1246 ================
   1247 */
   1248 #define	STAT_LAYOUTS		13
   1249 
   1250 void SCR_DrawLayout (void)
   1251 {
   1252 	if (!cl.frame.playerstate.stats[STAT_LAYOUTS])
   1253 		return;
   1254 	SCR_ExecuteLayoutString (cl.layout);
   1255 }
   1256 
   1257 //=======================================================
   1258 
   1259 /*
   1260 ==================
   1261 SCR_UpdateScreen
   1262 
   1263 This is called every frame, and can also be called explicitly to flush
   1264 text to the screen.
   1265 ==================
   1266 */
   1267 void SCR_UpdateScreen (void)
   1268 {
   1269 	int numframes;
   1270 	int i;
   1271 	float separation[2] = { 0, 0 };
   1272 
   1273 	// if the screen is disabled (loading plaque is up, or vid mode changing)
   1274 	// do nothing at all
   1275 	if (cls.disable_screen)
   1276 	{
   1277 		if (Sys_Milliseconds() - cls.disable_screen > 120000)
   1278 		{
   1279 			cls.disable_screen = 0;
   1280 			Com_Printf ("Loading plaque timed out.\n");
   1281 		}
   1282 		return;
   1283 	}
   1284 
   1285 	if (!scr_initialized || !con.initialized)
   1286 		return;				// not initialized yet
   1287 
   1288 	/*
   1289 	** range check cl_camera_separation so we don't inadvertently fry someone's
   1290 	** brain
   1291 	*/
   1292 	if ( cl_stereo_separation->value > 1.0 )
   1293 		Cvar_SetValue( "cl_stereo_separation", 1.0 );
   1294 	else if ( cl_stereo_separation->value < 0 )
   1295 		Cvar_SetValue( "cl_stereo_separation", 0.0 );
   1296 
   1297 	if ( cl_stereo->value )
   1298 	{
   1299 		numframes = 2;
   1300 		separation[0] = -cl_stereo_separation->value / 2;
   1301 		separation[1] =  cl_stereo_separation->value / 2;
   1302 	}		
   1303 	else
   1304 	{
   1305 		separation[0] = 0;
   1306 		separation[1] = 0;
   1307 		numframes = 1;
   1308 	}
   1309 
   1310 	for ( i = 0; i < numframes; i++ )
   1311 	{
   1312 		re.BeginFrame( separation[i] );
   1313 
   1314 		if (scr_draw_loading == 2)
   1315 		{	//  loading plaque over black screen
   1316 			int		w, h;
   1317 
   1318 			re.CinematicSetPalette(NULL);
   1319 			scr_draw_loading = false;
   1320 			re.DrawGetPicSize (&w, &h, "loading");
   1321 			re.DrawPic ((viddef.width-w)/2, (viddef.height-h)/2, "loading");
   1322 //			re.EndFrame();
   1323 //			return;
   1324 		} 
   1325 		// if a cinematic is supposed to be running, handle menus
   1326 		// and console specially
   1327 		else if (cl.cinematictime > 0)
   1328 		{
   1329 			if (cls.key_dest == key_menu)
   1330 			{
   1331 				if (cl.cinematicpalette_active)
   1332 				{
   1333 					re.CinematicSetPalette(NULL);
   1334 					cl.cinematicpalette_active = false;
   1335 				}
   1336 				M_Draw ();
   1337 //				re.EndFrame();
   1338 //				return;
   1339 			}
   1340 			else if (cls.key_dest == key_console)
   1341 			{
   1342 				if (cl.cinematicpalette_active)
   1343 				{
   1344 					re.CinematicSetPalette(NULL);
   1345 					cl.cinematicpalette_active = false;
   1346 				}
   1347 				SCR_DrawConsole ();
   1348 //				re.EndFrame();
   1349 //				return;
   1350 			}
   1351 			else
   1352 			{
   1353 				SCR_DrawCinematic();
   1354 //				re.EndFrame();
   1355 //				return;
   1356 			}
   1357 		}
   1358 		else 
   1359 		{
   1360 
   1361 			// make sure the game palette is active
   1362 			if (cl.cinematicpalette_active)
   1363 			{
   1364 				re.CinematicSetPalette(NULL);
   1365 				cl.cinematicpalette_active = false;
   1366 			}
   1367 
   1368 			// do 3D refresh drawing, and then update the screen
   1369 			SCR_CalcVrect ();
   1370 
   1371 			// clear any dirty part of the background
   1372 			SCR_TileClear ();
   1373 
   1374 			V_RenderView ( separation[i] );
   1375 
   1376 			SCR_DrawStats ();
   1377 			if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 1)
   1378 				SCR_DrawLayout ();
   1379 			if (cl.frame.playerstate.stats[STAT_LAYOUTS] & 2)
   1380 				CL_DrawInventory ();
   1381 
   1382 			SCR_DrawNet ();
   1383 			SCR_CheckDrawCenterString ();
   1384 
   1385 			if (scr_timegraph->value)
   1386 				SCR_DebugGraph (cls.frametime*300, 0);
   1387 
   1388 			if (scr_debuggraph->value || scr_timegraph->value || scr_netgraph->value)
   1389 				SCR_DrawDebugGraph ();
   1390 
   1391 			SCR_DrawPause ();
   1392 
   1393 			SCR_DrawConsole ();
   1394 
   1395 			M_Draw ();
   1396 
   1397 			SCR_DrawLoading ();
   1398 		}
   1399 	}
   1400 	re.EndFrame();
   1401 }