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