File: client\cl_scrn.c
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 }
1402