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 }