]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - cl_screen.c
74f00efcfb47369159cafdcea18c8cf8f3bc6f68
[xonotic/darkplaces.git] / cl_screen.c
1
2 #include "quakedef.h"
3
4 cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"};
5 cvar_t scr_fov = {CVAR_SAVE, "fov","90"};       // 10 - 170
6 cvar_t scr_conspeed = {CVAR_SAVE, "scr_conspeed","900"}; // LordHavoc: quake used 300
7 cvar_t scr_centertime = {0, "scr_centertime","2"};
8 cvar_t scr_showram = {CVAR_SAVE, "showram","1"};
9 cvar_t scr_showturtle = {CVAR_SAVE, "showturtle","0"};
10 cvar_t scr_showpause = {CVAR_SAVE, "showpause","1"};
11 cvar_t scr_printspeed = {0, "scr_printspeed","8"};
12 cvar_t scr_2dresolution = {CVAR_SAVE, "scr_2dresolution", "1"};
13 cvar_t cl_avidemo = {0, "cl_avidemo", "0"};
14
15 qboolean        scr_initialized;                // ready to draw
16
17 float           scr_con_current;
18 float           scr_conlines;           // lines of console to display
19
20 int                     clearconsole;
21 int                     clearnotify;
22
23 //qboolean      scr_disabled_for_loading;
24 qboolean        scr_drawloading = false;
25 //float         scr_disabled_time;
26
27 static qbyte menuplyr_pixels[4096];
28
29 void DrawCrosshair(int num);
30 void V_CalcRefdef (void);
31 static void SCR_ScreenShot_f (void);
32 static void R_Envmap_f (void);
33
34 // backend
35 void R_ClearScreen(void);
36
37 /*
38 ===============================================================================
39
40 CENTER PRINTING
41
42 ===============================================================================
43 */
44
45 char            scr_centerstring[1024];
46 float           scr_centertime_start;   // for slow victory printing
47 float           scr_centertime_off;
48 int                     scr_center_lines;
49 int                     scr_erase_lines;
50 int                     scr_erase_center;
51
52 /*
53 ==============
54 SCR_CenterPrint
55
56 Called for important messages that should stay in the center of the screen
57 for a few moments
58 ==============
59 */
60 void SCR_CenterPrint (char *str)
61 {
62         strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
63         scr_centertime_off = scr_centertime.value;
64         scr_centertime_start = cl.time;
65
66 // count the number of lines for centering
67         scr_center_lines = 1;
68         while (*str)
69         {
70                 if (*str == '\n')
71                         scr_center_lines++;
72                 str++;
73         }
74 }
75
76
77 void SCR_DrawCenterString (void)
78 {
79         char    *start;
80         int             l;
81         int             x, y;
82         int             remaining;
83
84 // the finale prints the characters one at a time
85         if (cl.intermission)
86                 remaining = scr_printspeed.value * (cl.time - scr_centertime_start);
87         else
88                 remaining = 9999;
89
90         scr_erase_center = 0;
91         start = scr_centerstring;
92
93         if (scr_center_lines <= 4)
94                 y = vid.conheight*0.35;
95         else
96                 y = 48;
97
98         do
99         {
100         // scan the width of the line
101                 for (l=0 ; l<40 ; l++)
102                         if (start[l] == '\n' || !start[l])
103                                 break;
104                 x = (vid.conwidth - l*8)/2;
105                 if (l > 0)
106                 {
107                         if (remaining < l)
108                                 l = remaining;
109                         DrawQ_String(x, y, start, l, 8, 8, 1, 1, 1, 1, 0);
110                         remaining -= l;
111                         if (remaining <= 0)
112                                 return;
113                 }
114
115                 y += 8;
116
117                 while (*start && *start != '\n')
118                         start++;
119
120                 if (!*start)
121                         break;
122                 start++;                // skip the \n
123         } while (1);
124 }
125
126 void SCR_CheckDrawCenterString (void)
127 {
128         if (scr_center_lines > scr_erase_lines)
129                 scr_erase_lines = scr_center_lines;
130
131         scr_centertime_off -= host_frametime;
132
133         // don't draw if this is a normal stats-screen intermission,
134         // only if it is not an intermission, or a finale intermission
135         if (cl.intermission == 1)
136                 return;
137         if (scr_centertime_off <= 0 && !cl.intermission)
138                 return;
139         if (key_dest != key_game)
140                 return;
141
142         SCR_DrawCenterString ();
143 }
144
145 /*
146 ==============
147 SCR_DrawRam
148 ==============
149 */
150 void SCR_DrawRam (void)
151 {
152 //      if (!scr_showram.integer)
153 //              return;
154 //      DrawQ_Pic (32, 0, "ram", 0, 0, 1, 1, 1, 1, 0);
155 }
156
157 /*
158 ==============
159 SCR_DrawTurtle
160 ==============
161 */
162 void SCR_DrawTurtle (void)
163 {
164         static int      count;
165
166         if (cls.state != ca_connected)
167                 return;
168
169         if (!scr_showturtle.integer)
170                 return;
171
172         if (host_frametime < 0.1)
173         {
174                 count = 0;
175                 return;
176         }
177
178         count++;
179         if (count < 3)
180                 return;
181
182         DrawQ_Pic (0, 0, "turtle", 0, 0, 1, 1, 1, 1, 0);
183 }
184
185 /*
186 ==============
187 SCR_DrawNet
188 ==============
189 */
190 void SCR_DrawNet (void)
191 {
192         if (cls.state != ca_connected)
193                 return;
194         if (realtime - cl.last_received_message < 0.3)
195                 return;
196         if (cls.demoplayback)
197                 return;
198
199         DrawQ_Pic (64, 0, "net", 0, 0, 1, 1, 1, 1, 0);
200 }
201
202 /*
203 ==============
204 DrawPause
205 ==============
206 */
207 void SCR_DrawPause (void)
208 {
209         cachepic_t      *pic;
210
211         if (cls.state != ca_connected)
212                 return;
213
214         if (!scr_showpause.integer)             // turn off for screenshots
215                 return;
216
217         if (!cl.paused)
218                 return;
219
220         pic = Draw_CachePic ("gfx/pause.lmp");
221         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/pause.lmp", 0, 0, 1, 1, 1, 1, 0);
222 }
223
224
225
226 /*
227 ==============
228 SCR_DrawLoading
229 ==============
230 */
231 void SCR_DrawLoading (void)
232 {
233         cachepic_t      *pic;
234
235         //if (!scr_drawloading)
236         //      return;
237
238         pic = Draw_CachePic ("gfx/loading.lmp");
239         DrawQ_Pic ((vid.conwidth - pic->width)/2, (vid.conheight - pic->height)/2, "gfx/loading.lmp", 0, 0, 1, 1, 1, 1, 0);
240 }
241
242
243
244 //=============================================================================
245
246
247 /*
248 ==================
249 SCR_SetUpToDrawConsole
250 ==================
251 */
252 void SCR_SetUpToDrawConsole (void)
253 {
254         Con_CheckResize ();
255
256 // decide on the height of the console
257         con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
258
259         if (con_forcedup)
260         {
261                 scr_conlines = vid.conheight;           // full screen
262                 scr_con_current = scr_conlines;
263         }
264         else if (key_dest == key_console)
265                 scr_conlines = vid.conheight/2; // half screen
266         else
267                 scr_conlines = 0;                               // none visible
268
269         if (scr_conlines < scr_con_current)
270         {
271                 scr_con_current -= scr_conspeed.value*host_realframetime;
272                 if (scr_conlines > scr_con_current)
273                         scr_con_current = scr_conlines;
274
275         }
276         else if (scr_conlines > scr_con_current)
277         {
278                 scr_con_current += scr_conspeed.value*host_realframetime;
279                 if (scr_conlines < scr_con_current)
280                         scr_con_current = scr_conlines;
281         }
282 }
283
284 /*
285 ==================
286 SCR_DrawConsole
287 ==================
288 */
289 void SCR_DrawConsole (void)
290 {
291         if (scr_con_current)
292         {
293                 Con_DrawConsole (scr_con_current);
294                 clearconsole = 0;
295         }
296         else
297         {
298                 if (key_dest == key_game || key_dest == key_message)
299                         Con_DrawNotify ();      // only draw notify in game
300         }
301 }
302
303 /*
304 ===============
305 SCR_BeginLoadingPlaque
306
307 ================
308 */
309 void SCR_BeginLoadingPlaque (void)
310 {
311         if (scr_drawloading)
312                 return;
313
314         S_StopAllSounds (true);
315
316 //      if (cls.state != ca_connected)
317 //              return;
318 //      if (cls.signon != SIGNONS)
319 //              return;
320
321 // redraw with no console and the loading plaque
322 //      Con_ClearNotify ();
323 //      scr_centertime_off = 0;
324 //      scr_con_current = 0;
325
326         scr_drawloading = true;
327         CL_UpdateScreen ();
328         scr_drawloading = true;
329         CL_UpdateScreen ();
330         //scr_drawloading = false;
331
332 //      scr_disabled_for_loading = true;
333 //      scr_disabled_time = realtime;
334 }
335
336 /*
337 ===============
338 SCR_EndLoadingPlaque
339
340 ================
341 */
342 void SCR_EndLoadingPlaque (void)
343 {
344         /*
345         if (!scr_drawloading)
346                 return;
347
348 //      scr_disabled_for_loading = false;
349         scr_drawloading = false;
350         Con_ClearNotify ();
351         */
352 }
353
354 //=============================================================================
355
356 char r_speeds_string[1024];
357 int speedstringcount, r_timereport_active;
358 double r_timereport_temp = 0, r_timereport_current = 0, r_timereport_start = 0;
359
360 void R_TimeReport(char *desc)
361 {
362         char tempbuf[256];
363         int length;
364         int t;
365
366         if (!r_timereport_active)
367                 return;
368
369         r_timereport_temp = r_timereport_current;
370         r_timereport_current = Sys_DoubleTime();
371         t = (int) ((r_timereport_current - r_timereport_temp) * 1000000.0);
372
373         sprintf(tempbuf, "%8i %s", t, desc);
374         length = strlen(tempbuf);
375         while (length < 20)
376                 tempbuf[length++] = ' ';
377         tempbuf[length] = 0;
378         if (speedstringcount + length > (vid.conwidth / 8))
379         {
380                 strcat(r_speeds_string, "\n");
381                 speedstringcount = 0;
382         }
383         // skip the space at the beginning if it's the first on the line
384         if (speedstringcount == 0)
385         {
386                 strcat(r_speeds_string, tempbuf + 1);
387                 speedstringcount = length - 1;
388         }
389         else
390         {
391                 strcat(r_speeds_string, tempbuf);
392                 speedstringcount += length;
393         }
394 }
395
396 void R_TimeReport_Start(void)
397 {
398         r_timereport_active = r_speeds.integer && cl.worldmodel && cls.state == ca_connected;
399         r_speeds_string[0] = 0;
400         if (r_timereport_active)
401         {
402                 speedstringcount = 0;
403                 AngleVectors (r_refdef.viewangles, vpn, NULL, NULL);
404                 //sprintf(r_speeds_string, "org:'%c%6.2f %c%6.2f %c%6.2f' ang:'%c%3.0f %c%3.0f %c%3.0f' dir:'%c%2.3f %c%2.3f %c%2.3f'\n%6i walls %6i dlitwalls %7i modeltris %7i meshtris\nBSP: %6i faces %6i nodes %6i leafs\n%4i models %4i bmodels %4i sprites %5i particles %3i dlights\n",
405                 //      r_refdef.vieworg[0] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[0]), r_refdef.vieworg[1] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[1]), r_refdef.vieworg[2] < 0 ? '-' : ' ', fabs(r_refdef.vieworg[2]),
406                 //      r_refdef.viewangles[0] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[0]), r_refdef.viewangles[1] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[1]), r_refdef.viewangles[2] < 0 ? '-' : ' ', fabs(r_refdef.viewangles[2]),
407                 //      vpn[0] < 0 ? '-' : ' ', fabs(vpn[0]), vpn[1] < 0 ? '-' : ' ', fabs(vpn[1]), vpn[2] < 0 ? '-' : ' ', fabs(vpn[2]),
408                 sprintf(r_speeds_string,
409                         "org:'%+8.2f %+8.2f %+8.2f' ang:'%+4.0f %+4.0f %+4.0f' dir:'%+2.3f %+2.3f %+2.3f'\n"
410                         "world:%6i faces%6i nodes%6i leafs%6i walls%6i dlitwalls\n"
411                         "%5i models%5i bmodels%5i sprites%6i particles%4i dlights\n"
412                         "%6i modeltris%6i transmeshs%6i transtris%6i meshs%6i meshtris\n",
413                         r_refdef.vieworg[0], r_refdef.vieworg[1], r_refdef.vieworg[2], r_refdef.viewangles[0], r_refdef.viewangles[1], r_refdef.viewangles[2], vpn[0], vpn[1], vpn[2],
414                         c_faces, c_nodes, c_leafs, c_brush_polys, c_light_polys,
415                         c_models, c_bmodels, c_sprites, c_particles, c_dlights,
416                         c_alias_polys, c_transmeshs, c_transtris, c_meshs, c_meshtris);
417
418                 c_brush_polys = 0;
419                 c_alias_polys = 0;
420                 c_light_polys = 0;
421                 c_faces = 0;
422                 c_nodes = 0;
423                 c_leafs = 0;
424                 c_models = 0;
425                 c_bmodels = 0;
426                 c_sprites = 0;
427                 c_particles = 0;
428         //      c_dlights = 0;
429
430                 r_timereport_start = Sys_DoubleTime();
431         }
432 }
433
434 void R_TimeReport_End(void)
435 {
436         r_timereport_current = r_timereport_start;
437         R_TimeReport("total");
438
439         if (r_timereport_active)
440         {
441                 int i, j, lines, y;
442                 lines = 1;
443                 for (i = 0;r_speeds_string[i];i++)
444                         if (r_speeds_string[i] == '\n')
445                                 lines++;
446                 y = vid.conheight - sb_lines - lines * 8/* - 8*/;
447                 i = j = 0;
448                 DrawQ_Fill(0, y, vid.conwidth, lines * 8, 0, 0, 0, 0.5, 0);
449                 while (r_speeds_string[i])
450                 {
451                         j = i;
452                         while (r_speeds_string[i] && r_speeds_string[i] != '\n')
453                                 i++;
454                         if (i - j > 0)
455                                 DrawQ_String(0, y, r_speeds_string + j, i - j, 8, 8, 1, 1, 1, 1, 0);
456                         if (r_speeds_string[i] == '\n')
457                                 i++;
458                         y += 8;
459                 }
460         }
461 }
462
463 /*
464 =================
465 SCR_SizeUp_f
466
467 Keybinding command
468 =================
469 */
470 void SCR_SizeUp_f (void)
471 {
472         Cvar_SetValue ("viewsize",scr_viewsize.value+10);
473 }
474
475
476 /*
477 =================
478 SCR_SizeDown_f
479
480 Keybinding command
481 =================
482 */
483 void SCR_SizeDown_f (void)
484 {
485         Cvar_SetValue ("viewsize",scr_viewsize.value-10);
486 }
487
488 void CL_Screen_Init(void)
489 {
490         qpic_t *dat;
491
492         Cvar_RegisterVariable (&scr_fov);
493         Cvar_RegisterVariable (&scr_viewsize);
494         Cvar_RegisterVariable (&scr_conspeed);
495         Cvar_RegisterVariable (&scr_showram);
496         Cvar_RegisterVariable (&scr_showturtle);
497         Cvar_RegisterVariable (&scr_showpause);
498         Cvar_RegisterVariable (&scr_centertime);
499         Cvar_RegisterVariable (&scr_printspeed);
500         Cvar_RegisterVariable (&scr_2dresolution);
501         Cvar_RegisterVariable (&cl_avidemo);
502
503         Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
504         Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
505         Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
506         Cmd_AddCommand ("envmap", R_Envmap_f);
507
508         scr_initialized = true;
509
510         // HACK HACK HACK
511         // load the image data for the player image in the config menu
512         dat = (qpic_t *)COM_LoadFile ("gfx/menuplyr.lmp", false);
513         if (!dat)
514                 Sys_Error("unable to load gfx/menuplyr.lmp");
515         SwapPic (dat);
516
517         if (dat->width*dat->height <= 4096)
518                 memcpy (menuplyr_pixels, dat->data, dat->width * dat->height);
519         else
520                 Con_Printf("gfx/menuplyr.lmp larger than 4k buffer");
521         Mem_Free(dat);
522 }
523
524 void DrawQ_Clear(void)
525 {
526         r_refdef.drawqueuesize = 0;
527 }
528
529 void DrawQ_Pic(float x, float y, char *picname, float width, float height, float red, float green, float blue, float alpha, int flags)
530 {
531         int size;
532         drawqueue_t *dq;
533         if (alpha < (1.0f / 255.0f))
534                 return;
535         size = sizeof(*dq) + ((strlen(picname) + 1 + 3) & ~3);
536         if (r_refdef.drawqueuesize + size > MAX_DRAWQUEUE)
537                 return;
538         red = bound(0, red, 1);
539         green = bound(0, green, 1);
540         blue = bound(0, blue, 1);
541         alpha = bound(0, alpha, 1);
542         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
543         dq->size = size;
544         dq->command = DRAWQUEUE_PIC;
545         dq->flags = flags;
546         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
547         dq->x = x;
548         dq->y = y;
549         // if these are not zero, they override the pic's size
550         dq->scalex = width;
551         dq->scaley = height;
552         strcpy((char *)(dq + 1), picname);
553         r_refdef.drawqueuesize += dq->size;
554 }
555
556 void DrawQ_String(float x, float y, char *string, int maxlen, float scalex, float scaley, float red, float green, float blue, float alpha, int flags)
557 {
558         int size, len;
559         drawqueue_t *dq;
560         char *out;
561         if (alpha < (1.0f / 255.0f))
562                 return;
563         if (maxlen < 1)
564                 len = strlen(string);
565         else
566                 for (len = 0;len < maxlen && string[len];len++);
567         for (;len > 0 && string[0] == ' ';string++, x += scalex, len--);
568         for (;len > 0 && string[len - 1] == ' ';len--);
569         if (len < 1)
570                 return;
571         if (x >= vid.conwidth || y >= vid.conheight || x < (-scalex * maxlen) || y < (-scaley))
572                 return;
573         size = sizeof(*dq) + ((len + 1 + 3) & ~3);
574         if (r_refdef.drawqueuesize + size > MAX_DRAWQUEUE)
575                 return;
576         red = bound(0, red, 1);
577         green = bound(0, green, 1);
578         blue = bound(0, blue, 1);
579         alpha = bound(0, alpha, 1);
580         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
581         dq->size = size;
582         dq->command = DRAWQUEUE_STRING;
583         dq->flags = flags;
584         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
585         dq->x = x;
586         dq->y = y;
587         dq->scalex = scalex;
588         dq->scaley = scaley;
589         out = (char *)(dq + 1);
590         memcpy(out, string, len);
591         out[len] = 0;
592         r_refdef.drawqueuesize += dq->size;
593 }
594
595 void DrawQ_Fill (float x, float y, float w, float h, float red, float green, float blue, float alpha, int flags)
596 {
597         int size;
598         drawqueue_t *dq;
599         if (alpha < (1.0f / 255.0f))
600                 return;
601         size = sizeof(*dq) + 4;
602         if (r_refdef.drawqueuesize + size > MAX_DRAWQUEUE)
603                 return;
604         red = bound(0, red, 1);
605         green = bound(0, green, 1);
606         blue = bound(0, blue, 1);
607         alpha = bound(0, alpha, 1);
608         dq = (void *)(r_refdef.drawqueue + r_refdef.drawqueuesize);
609         dq->size = size;
610         dq->command = DRAWQUEUE_PIC;
611         dq->flags = flags;
612         dq->color = ((unsigned int) (red * 255.0f) << 24) | ((unsigned int) (green * 255.0f) << 16) | ((unsigned int) (blue * 255.0f) << 8) | ((unsigned int) (alpha * 255.0f));
613         dq->x = x;
614         dq->y = y;
615         dq->scalex = w;
616         dq->scaley = h;
617         // empty pic name
618         *((char *)(dq + 1)) = 0;
619         r_refdef.drawqueuesize += dq->size;
620 }
621
622 //only used for the player color selection menu
623 void DrawQ_PicTranslate (int x, int y, char *picname, qbyte *translation)
624 {
625         int i, c;
626         unsigned int trans[4096];
627         cachepic_t *pic;
628
629         pic = Draw_CachePic(picname);
630         if (pic == NULL)
631                 return;
632
633         c = pic->width * pic->height;
634         if (c > 4096)
635         {
636                 Con_Printf("DrawQ_PicTranslate: image larger than 4k buffer\n");
637                 return;
638         }
639
640         for (i = 0;i < c;i++)
641                 trans[i] = d_8to24table[translation[menuplyr_pixels[i]]];
642
643         // FIXME: this is renderer stuff?
644         R_UpdateTexture (pic->tex, (qbyte *)trans);
645
646         DrawQ_Pic(x, y, picname, 0, 0, 1, 1, 1, 1, 0);
647 }
648
649
650 /*
651 ====================
652 CalcFov
653 ====================
654 */
655 float CalcFov (float fov_x, float width, float height)
656 {
657         // calculate vision size and alter by aspect, then convert back to angle
658         return atan (height / (width / tan(fov_x/360*M_PI))) * 360 / M_PI;
659 }
660
661 /*
662 =================
663 SCR_CalcRefdef
664
665 Must be called whenever vid changes
666 Internal use only
667 =================
668 */
669 static void SCR_CalcRefdef (void)
670 {
671         float size;
672         int contents;
673
674 //========================================
675
676 // bound viewsize
677         if (scr_viewsize.value < 30)
678                 Cvar_Set ("viewsize","30");
679         if (scr_viewsize.value > 120)
680                 Cvar_Set ("viewsize","120");
681
682 // bound field of view
683         if (scr_fov.value < 10)
684                 Cvar_Set ("fov","10");
685         if (scr_fov.value > 170)
686                 Cvar_Set ("fov","170");
687
688 // intermission is always full screen
689         if (cl.intermission)
690         {
691                 size = 1;
692                 sb_lines = 0;
693         }
694         else
695         {
696                 if (scr_viewsize.value >= 120)
697                         sb_lines = 0;           // no status bar at all
698                 else if (scr_viewsize.value >= 110)
699                         sb_lines = 24;          // no inventory
700                 else
701                         sb_lines = 24+16+8;
702                 size = scr_viewsize.value * (1.0 / 100.0);
703         }
704
705         if (size >= 1)
706         {
707                 r_refdef.width = vid.realwidth;
708                 r_refdef.height = vid.realheight;
709                 r_refdef.x = 0;
710                 r_refdef.y = 0;
711         }
712         else
713         {
714                 r_refdef.width = vid.realwidth * size;
715                 r_refdef.height = vid.realheight * size;
716                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
717                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
718         }
719
720         r_refdef.width = bound(0, r_refdef.width, vid.realwidth);
721         r_refdef.height = bound(0, r_refdef.height, vid.realheight);
722         r_refdef.x = bound(0, r_refdef.x, vid.realwidth - r_refdef.width) + vid.realx;
723         r_refdef.y = bound(0, r_refdef.y, vid.realheight - r_refdef.height) + vid.realy;
724
725         // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
726         r_refdef.fov_x = scr_fov.value * cl.viewzoom;
727         r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
728
729         if (cl.worldmodel)
730         {
731                 Mod_CheckLoaded(cl.worldmodel);
732                 contents = Mod_PointInLeaf(r_refdef.vieworg, cl.worldmodel)->contents;
733                 if (contents != CONTENTS_EMPTY && contents != CONTENTS_SOLID)
734                 {
735                         r_refdef.fov_x *= (sin(cl.time * 4.7) * 0.015 + 0.985);
736                         r_refdef.fov_y *= (sin(cl.time * 3.0) * 0.015 + 0.985);
737                 }
738         }
739 }
740
741 /*
742 ==================
743 SCR_ScreenShot_f
744 ==================
745 */
746 void SCR_ScreenShot_f (void)
747 {
748         int i;
749         char filename[16];
750         char checkname[MAX_OSPATH];
751 //
752 // find a file name to save it to
753 //
754         strcpy(filename, "dp0000.tga");
755
756         for (i=0 ; i<=9999 ; i++)
757         {
758                 filename[2] = (i/1000)%10 + '0';
759                 filename[3] = (i/ 100)%10 + '0';
760                 filename[4] = (i/  10)%10 + '0';
761                 filename[5] = (i/   1)%10 + '0';
762                 sprintf (checkname, "%s/%s", com_gamedir, filename);
763                 if (Sys_FileTime(checkname) == -1)
764                         break;  // file doesn't exist
765         }
766         if (i==10000)
767         {
768                 Con_Printf ("SCR_ScreenShot_f: Couldn't create a TGA file\n");
769                 return;
770         }
771
772         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
773                 Con_Printf ("Wrote %s\n", filename);
774         else
775                 Con_Printf ("unable to write %s\n", filename);
776 }
777
778 static int cl_avidemo_frame = 0;
779
780 void SCR_CaptureAVIDemo(void)
781 {
782         char filename[32];
783         sprintf(filename, "dpavi%06d.tga", cl_avidemo_frame);
784         if (SCR_ScreenShot(filename, vid.realx, vid.realy, vid.realwidth, vid.realheight))
785                 cl_avidemo_frame++;
786         else
787         {
788                 Cvar_SetValueQuick(&cl_avidemo, 0);
789                 Con_Printf("avi saving failed on frame %i, out of disk space?  stopping avi demo catpure.\n", cl_avidemo_frame);
790                 cl_avidemo_frame = 0;
791         }
792 }
793
794 /*
795 ===============
796 R_Envmap_f
797
798 Grab six views for environment mapping tests
799 ===============
800 */
801 struct
802 {
803         float angles[3];
804         char *name;
805 }
806 envmapinfo[6] =
807 {
808         {{  0,   0, 0}, "ft"},
809         {{  0,  90, 0}, "rt"},
810         {{  0, 180, 0}, "bk"},
811         {{  0, 270, 0}, "lf"},
812         {{-90,  90, 0}, "up"},
813         {{ 90,  90, 0}, "dn"}
814 };
815
816 static void R_Envmap_f (void)
817 {
818         int j, size;
819         char filename[256], basename[256];
820
821         if (Cmd_Argc() != 3)
822         {
823                 Con_Printf ("envmap <basename> <size>: save out 6 cubic environment map images, usable with loadsky, note that size must one of 128, 256, 512, or 1024 and can't be bigger than your current resolution\n");
824                 return;
825         }
826
827         strcpy(basename, Cmd_Argv(1));
828         size = atoi(Cmd_Argv(2));
829         if (size != 128 && size != 256 && size != 512 && size != 1024)
830         {
831                 Con_Printf("envmap: size must be one of 128, 256, 512, or 1024\n");
832                 return;
833         }
834         if (size > vid.realwidth || size > vid.realheight)
835         {
836                 Con_Printf("envmap: your resolution is not big enough to render that size\n");
837                 return;
838         }
839
840         envmap = true;
841
842         r_refdef.x = 0;
843         r_refdef.y = 0;
844         r_refdef.width = size;
845         r_refdef.height = size;
846
847         r_refdef.fov_x = 90;
848         r_refdef.fov_y = 90;
849
850         for (j = 0;j < 6;j++)
851         {
852                 sprintf(filename, "env/%s%s.tga", basename, envmapinfo[j].name);
853                 VectorCopy(envmapinfo[j].angles, r_refdef.viewangles);
854                 R_ClearScreen();
855                 R_RenderView ();
856                 SCR_ScreenShot(filename, vid.realx, vid.realy, size, size);
857         }
858
859         envmap = false;
860 }
861
862 //=============================================================================
863
864 // LordHavoc: SHOWLMP stuff
865 #define SHOWLMP_MAXLABELS 256
866 typedef struct showlmp_s
867 {
868         qboolean        isactive;
869         float           x;
870         float           y;
871         char            label[32];
872         char            pic[128];
873 }
874 showlmp_t;
875
876 showlmp_t showlmp[SHOWLMP_MAXLABELS];
877
878 void SHOWLMP_decodehide(void)
879 {
880         int i;
881         qbyte *lmplabel;
882         lmplabel = MSG_ReadString();
883         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
884                 if (showlmp[i].isactive && strcmp(showlmp[i].label, lmplabel) == 0)
885                 {
886                         showlmp[i].isactive = false;
887                         return;
888                 }
889 }
890
891 void SHOWLMP_decodeshow(void)
892 {
893         int i, k;
894         qbyte lmplabel[256], picname[256];
895         float x, y;
896         strcpy(lmplabel,MSG_ReadString());
897         strcpy(picname, MSG_ReadString());
898         if (gamemode == GAME_NEHAHRA) // LordHavoc: nasty old legacy junk
899         {
900                 x = MSG_ReadByte();
901                 y = MSG_ReadByte();
902         }
903         else
904         {
905                 x = MSG_ReadShort();
906                 y = MSG_ReadShort();
907         }
908         k = -1;
909         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
910                 if (showlmp[i].isactive)
911                 {
912                         if (strcmp(showlmp[i].label, lmplabel) == 0)
913                         {
914                                 k = i;
915                                 break; // drop out to replace it
916                         }
917                 }
918                 else if (k < 0) // find first empty one to replace
919                         k = i;
920         if (k < 0)
921                 return; // none found to replace
922         // change existing one
923         showlmp[k].isactive = true;
924         strcpy(showlmp[k].label, lmplabel);
925         strcpy(showlmp[k].pic, picname);
926         showlmp[k].x = x;
927         showlmp[k].y = y;
928 }
929
930 void SHOWLMP_drawall(void)
931 {
932         int i;
933         if (cl.worldmodel)
934                 for (i = 0;i < SHOWLMP_MAXLABELS;i++)
935                         if (showlmp[i].isactive)
936                                 DrawQ_Pic(showlmp[i].x, showlmp[i].y, showlmp[i].pic, 0, 0, 1, 1, 1, 1, 0);
937 }
938
939 void SHOWLMP_clear(void)
940 {
941         int i;
942         for (i = 0;i < SHOWLMP_MAXLABELS;i++)
943                 showlmp[i].isactive = false;
944 }
945
946 void CL_SetupScreenSize(void)
947 {
948         static float old2dresolution = -1;
949
950         VID_GetWindowSize (&vid.realx, &vid.realy, &vid.realwidth, &vid.realheight);
951
952         VID_UpdateGamma(false);
953
954         if (scr_2dresolution.value != old2dresolution)
955         {
956                 Cvar_SetValue("scr_2dresolution", bound(0.0f, scr_2dresolution.value, 1.0f));
957                 old2dresolution = scr_2dresolution.value;
958         }
959
960         if (vid.realwidth > 320)
961         {
962                 vid.conwidth = (vid.realwidth - 320) * scr_2dresolution.value + 320;
963                 vid.conwidth = bound(320, vid.conwidth, vid.realwidth);
964         }
965         else
966                 vid.conwidth = 320;
967
968         if (vid.realheight > 240)
969         {
970                 vid.conheight = (vid.realheight - 240) * scr_2dresolution.value + 240;
971                 vid.conheight = bound(240, vid.conheight, vid.realheight);
972         }
973         else
974                 vid.conheight = 240;
975
976         SCR_SetUpToDrawConsole();
977
978         // determine size of refresh window
979         SCR_CalcRefdef();
980 }
981
982 void CL_UpdateScreen(void)
983 {
984         if (!scr_initialized || !con_initialized)
985                 return;                         // not initialized yet
986
987         if (cl_avidemo.integer)
988                 SCR_CaptureAVIDemo();
989         else
990                 cl_avidemo_frame = 0;
991
992         R_TimeReport("other");
993
994         CL_SetupScreenSize();
995
996         DrawQ_Clear();
997
998         V_UpdateBlends();
999         V_CalcRefdef ();
1000
1001         R_TimeReport("setup");
1002
1003         SCR_DrawRam ();
1004         SCR_DrawNet ();
1005         SCR_DrawTurtle ();
1006         SCR_DrawPause ();
1007
1008         Sbar_Draw();
1009
1010         SCR_CheckDrawCenterString();
1011         SHOWLMP_drawall();
1012
1013         SCR_DrawConsole();
1014
1015         ui_draw();
1016
1017         if (scr_drawloading)
1018         {
1019                 scr_drawloading = false;
1020                 SCR_DrawLoading();
1021         }
1022
1023         R_TimeReport("2d");
1024
1025         // add r_speeds text to queue
1026         R_TimeReport_End();
1027
1028         // start a new timing run
1029         R_TimeReport_Start();
1030
1031         // make menu fade everything else on the screen
1032         M_Draw();
1033
1034         SCR_UpdateScreen();
1035 }
1036
1037 void CL_Screen_NewMap(void)
1038 {
1039         SHOWLMP_clear();
1040 }