cl_scrn.c 27 KB

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