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