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