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