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