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