1 /*
2 * screen.c -- master for refresh, status bar, console, chat, notify, etc
3 * $Id: screen.c 6000 2017-11-28 15:50:03Z sezero $
4 *
5 * Copyright (C) 1996-1997 Id Software, Inc.
6 * Copyright (C) 1997-1998 Raven Software Corp.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 *
17 * See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /*=============================================================================
25
26 background clear
27 rendering
28 turtle/net/ram icons
29 sbar
30 centerprint / slow centerprint
31 notify lines
32 intermission / finale overlay
33 loading plaque
34 console
35 menu
36
37 required background clears
38 required update regions
39
40 syncronous draw mode or async
41 One off screen buffer, with updates either copied or xblited
42 Need to double buffer?
43
44 async draw will require the refresh area to be cleared, because
45 it will be xblited, but sync draw can just ignore it.
46
47 sync
48 draw
49
50 CenterPrint ()
51 SlowPrint ()
52 Screen_Update ();
53 Con_Printf ();
54
55 net
56 turn off messages option
57
58 the refresh is always rendered, unless the console is full screen
59
60 console is:
61 notify lines
62 half
63 full
64
65 =============================================================================*/
66
67 #include "quakedef.h"
68 //#include "r_local.h"
69 #ifdef PLATFORM_WINDOWS
70 #include "winquake.h"
71 #endif
72
73
74 static qboolean scr_initialized; // ready to draw
75
76 vrect_t scr_vrect;
77 //vrect_t *pconupdate;
78
79 /* these are only functional in the software renderer */
80 int scr_copytop; // only the refresh window will be updated
81 int scr_copyeverything; // unless these variables are flagged
82 int scr_topupdate;
83 int scr_fullupdate;
84 static qboolean scr_needfull = false;
85
86 static int clearconsole;
87 int clearnotify;
88
89 float scr_con_current;
90 float scr_conlines; // lines of console to display
91
92 int trans_level = 0;
93
94 cvar_t scr_viewsize = {"viewsize", "110", CVAR_ARCHIVE};
95 cvar_t scr_fov = {"fov", "90", CVAR_NONE}; // 10 - 170
96 cvar_t scr_fov_adapt = {"fov_adapt", "1", CVAR_ARCHIVE}; // "Hor+" scaling
97 cvar_t scr_contrans = {"contrans", "0", CVAR_ARCHIVE};
98 static cvar_t scr_conspeed = {"scr_conspeed", "300", CVAR_NONE};
99 static cvar_t scr_centertime = {"scr_centertime", "4", CVAR_NONE};
100 static cvar_t scr_showram = {"showram", "1", CVAR_NONE};
101 static cvar_t scr_showturtle = {"showturtle", "0", CVAR_NONE};
102 static cvar_t scr_showpause = {"showpause", "1", CVAR_NONE};
103 static cvar_t scr_showfps = {"showfps", "0", CVAR_NONE};
104
105 #if !defined(H2W)
106 static qboolean scr_drawloading;
107 static float scr_disabled_time;
108 int total_loading_size, current_loading_size, loading_stage;
109 #endif /* H2W */
110 qboolean scr_disabled_for_loading;
111 qboolean scr_skipupdate;
112 qboolean block_drawing;
113
114 static qpic_t *scr_ram;
115 static qpic_t *scr_net;
116 static qpic_t *scr_turtle;
117
118 static void SCR_ScreenShot_f (void);
119
120 static const char *plaquemessage = ""; // pointer to current plaque message
121
122 static void Plaque_Draw (const char *message, qboolean AlwaysDraw);
123 #if !defined(H2W)
124 /* procedures for the mission pack intro messages and objectives */
125 static void Info_Plaque_Draw (const char *message);
126 static void Bottom_Plaque_Draw (const char *message);
127 #endif /* H2W */
128
129
130 /*
131 ===============================================================================
132
133 CENTER PRINTING
134
135 ===============================================================================
136 */
137
138 static char scr_centerstring[1024];
139 float scr_centertime_off;
140 static int scr_center_lines;
141 static int scr_erase_lines;
142 //static int scr_erase_center;
143
144 #define MAXLINES 27
145 static int lines;
146 static int StartC[MAXLINES], EndC[MAXLINES];
147
148 #if !defined(H2W)
149 /* mission pack objectives: */
150 #define MAX_INFO 1024
151 static char infomessage[MAX_INFO];
152
UpdateInfoMessage(void)153 static void UpdateInfoMessage (void)
154 {
155 unsigned int i, check;
156 const char *newmessage;
157
158 q_strlcpy(infomessage, "Objectives:", sizeof(infomessage));
159
160 if (!info_string_count)
161 return;
162
163 for (i = 0; i < 32; i++)
164 {
165 check = (1 << i);
166
167 if (cl.info_mask & check)
168 {
169 newmessage = CL_GetInfoString(i);
170 q_strlcat(infomessage, "@@", sizeof(infomessage));
171 q_strlcat(infomessage, newmessage, sizeof(infomessage));
172 }
173 }
174
175 for (i = 0; i < 32; i++)
176 {
177 check = (1 << i);
178
179 if (cl.info_mask2 & check)
180 {
181 newmessage = CL_GetInfoString(i + 32);
182 q_strlcat(infomessage, "@@", sizeof(infomessage));
183 q_strlcat(infomessage, newmessage, sizeof(infomessage));
184 }
185 }
186 }
187 #endif /* H2W */
188
FindTextBreaks(const char * message,int Width)189 static void FindTextBreaks (const char *message, int Width)
190 {
191 int pos, start, lastspace, oldlast;
192
193 lines = pos = start = 0;
194 lastspace = -1;
195
196 while (1)
197 {
198 if (pos-start >= Width || message[pos] == '@' || message[pos] == 0)
199 {
200 oldlast = lastspace;
201 if (message[pos] == '@' || lastspace == -1 || message[pos] == 0)
202 lastspace = pos;
203
204 StartC[lines] = start;
205 EndC[lines] = lastspace;
206 lines++;
207 if (lines == MAXLINES)
208 return;
209 if (message[pos] == '@')
210 start = pos + 1;
211 else if (oldlast == -1)
212 start = lastspace;
213 else
214 start = lastspace + 1;
215
216 lastspace = -1;
217 }
218
219 if (message[pos] == 32)
220 lastspace = pos;
221 else if (message[pos] == 0)
222 break;
223
224 pos++;
225 }
226 }
227
228 /*
229 ==============
230 SCR_CenterPrint
231
232 Called for important messages that should stay in the center of the screen
233 for a few moments
234 ==============
235 */
SCR_CenterPrint(const char * str)236 void SCR_CenterPrint (const char *str)
237 {
238 strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
239 scr_centertime_off = scr_centertime.value;
240
241 FindTextBreaks(scr_centerstring, 38);
242 scr_center_lines = lines;
243 }
244
245 /*
246 static void SCR_EraseCenterString (void)
247 {
248 int y, height;
249
250 if (scr_erase_center++ > vid.numpages)
251 {
252 scr_erase_lines = 0;
253 return;
254 }
255
256 // y = (25-lines) * 8 / 2;
257 y = (scr_center_lines <= 4) ? vid.height*0.35 : 48;
258
259 scr_copytop = 1;
260 height = q_min(8 * scr_erase_lines, vid.height - y - 1);
261 Draw_TileClear (0, y, vid.width, height);
262 }
263 */
264
SCR_DrawCenterString(void)265 static void SCR_DrawCenterString (void)
266 {
267 int i, cnt;
268 int bx, by;
269 char temp[80];
270
271 // scr_erase_center = 0;
272
273 FindTextBreaks(scr_centerstring, 38);
274
275 by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1);
276
277 for (i = 0; i < lines; i++, by += 8)
278 {
279 cnt = EndC[i] - StartC[i];
280 strncpy (temp, &scr_centerstring[StartC[i]], cnt);
281 temp[cnt] = 0;
282 bx = (40-strlen(temp)) * 8 / 2;
283 M_Print (bx, by, temp);
284 }
285 }
286
SCR_CheckDrawCenterString2(void)287 static qboolean SCR_CheckDrawCenterString2 (void)
288 {
289 scr_copytop = 1;
290 if (scr_center_lines > scr_erase_lines)
291 scr_erase_lines = scr_center_lines;
292
293 scr_centertime_off -= host_frametime;
294
295 if (scr_centertime_off <= 0 && !cl.intermission)
296 return false;
297 if (Key_GetDest() != key_game)
298 return false;
299
300 return true;
301 }
302
SCR_CheckDrawCenterString(void)303 static void SCR_CheckDrawCenterString (void)
304 {
305 if (! SCR_CheckDrawCenterString2())
306 return;
307 #if !defined(H2W)
308 if (intro_playing)
309 {
310 Bottom_Plaque_Draw(scr_centerstring);
311 return;
312 }
313 #endif /* H2W */
314 SCR_DrawCenterString ();
315 }
316
317 //=============================================================================
318
319
320 /*
321 ====================
322 AdaptFovx
323 Adapt a 4:3 horizontal FOV to the current screen size using the "Hor+" scaling:
324 2.0 * atan(width / height * 3.0 / 4.0 * tan(fov43 / 2.0))
325 ====================
326 */
AdaptFovx(float fov_x,float width,float height)327 static float AdaptFovx (float fov_x, float width, float height)
328 {
329 float a, x;
330
331 if (fov_x < 1 || fov_x > 179)
332 Sys_Error ("Bad fov: %f", fov_x);
333
334 #if defined(PLATFORM_DOS) || defined(SVGAQUAKE)
335 if (vid.aspect > 1.10f)
336 return fov_x; /* no fov_adapt for weird VGA modes */
337 #endif
338 #ifdef PLATFORM_AMIGAOS3
339 if (vid.noadapt)
340 return fov_x; /* not for Amiga native chipset modes */
341 #endif
342 if (!scr_fov_adapt.integer)
343 return fov_x;
344 if ((x = height / width) == 0.75)
345 return fov_x;
346 a = atan(0.75 / x * tan(fov_x / 360 * M_PI));
347 a = a * 360 / M_PI;
348 return a;
349 }
350
351 /*
352 ====================
353 CalcFovy
354 ====================
355 */
CalcFovy(float fov_x,float width,float height)356 static float CalcFovy (float fov_x, float width, float height)
357 {
358 float a, x;
359
360 if (fov_x < 1 || fov_x > 179)
361 Sys_Error ("Bad fov: %f", fov_x);
362
363 x = width / tan(fov_x / 360 * M_PI);
364 a = atan(height / x);
365 a = a * 360 / M_PI;
366 return a;
367 }
368
369 /*
370 =================
371 SCR_CalcRefdef
372
373 Must be called whenever vid changes
374 Internal use only
375 =================
376 */
SCR_CalcRefdef(void)377 static void SCR_CalcRefdef (void)
378 {
379 vrect_t vrect;
380
381 scr_fullupdate = 0; // force a background redraw
382
383 // bound viewsize
384 if (scr_viewsize.integer < 30)
385 Cvar_SetQuick (&scr_viewsize, "30");
386 else if (scr_viewsize.integer > 130)
387 Cvar_SetQuick (&scr_viewsize, "130");
388
389 // bound field of view
390 if (scr_fov.integer < 10)
391 Cvar_SetQuick (&scr_fov, "10");
392 else if (scr_fov.integer > 110)
393 Cvar_SetQuick (&scr_fov, "110");
394
395 vid.recalc_refdef = 0;
396
397 // force the status bar to redraw
398 SB_ViewSizeChanged ();
399 Sbar_Changed();
400
401 // intermission is always full screen
402 if (scr_viewsize.integer >= 110 || cl.intermission)
403 sb_lines = 0; // no status bar
404 else
405 sb_lines = 46;
406
407 // these calculations mirror those in R_Init() for r_refdef, but take no
408 // account of water warping
409 vrect.x = 0;
410 vrect.y = 0;
411 vrect.width = vid.width;
412 vrect.height = vid.height;
413
414 R_SetVrect (&vrect, &scr_vrect, sb_lines);
415 r_refdef.vrect = scr_vrect;
416 r_refdef.fov_x = AdaptFovx (scr_fov.value, r_refdef.vrect.width, r_refdef.vrect.height);
417 r_refdef.fov_y = CalcFovy (r_refdef.fov_x, r_refdef.vrect.width, r_refdef.vrect.height);
418
419 // guard against going from one mode to another that's less than half the
420 // vertical resolution
421 if (scr_con_current > vid.height)
422 scr_con_current = vid.height;
423
424 // notify the refresh of the change
425 R_ViewChanged (vid.aspect);
426 }
427
428 //=============================================================================
429
430
431 /*
432 =================
433 SCR_SizeUp_f
434
435 Keybinding command
436 =================
437 */
SCR_SizeUp_f(void)438 static void SCR_SizeUp_f (void)
439 {
440 Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer + 10);
441 }
442
443 /*
444 =================
445 SCR_SizeDown_f
446
447 Keybinding command
448 =================
449 */
SCR_SizeDown_f(void)450 static void SCR_SizeDown_f (void)
451 {
452 Cvar_SetValueQuick (&scr_viewsize, scr_viewsize.integer - 10);
453 }
454
SCR_Callback_refdef(cvar_t * var)455 static void SCR_Callback_refdef (cvar_t *var)
456 {
457 vid.recalc_refdef = 1;
458 }
459
460 //=============================================================================
461
462
463 /*
464 ==================
465 SCR_Init
466 ==================
467 */
SCR_Init(void)468 void SCR_Init (void)
469 {
470 scr_ram = Draw_PicFromWad ("ram");
471 scr_net = Draw_PicFromWad ("net");
472 scr_turtle = Draw_PicFromWad ("turtle");
473
474 if (draw_reinit)
475 return;
476
477 Cvar_SetCallback (&scr_fov, SCR_Callback_refdef);
478 Cvar_SetCallback (&scr_fov_adapt, SCR_Callback_refdef);
479 Cvar_SetCallback (&scr_viewsize, SCR_Callback_refdef);
480 Cvar_RegisterVariable (&scr_fov);
481 Cvar_RegisterVariable (&scr_fov_adapt);
482 Cvar_RegisterVariable (&scr_viewsize);
483 Cvar_RegisterVariable (&scr_contrans);
484 Cvar_RegisterVariable (&scr_conspeed);
485 Cvar_RegisterVariable (&scr_showram);
486 Cvar_RegisterVariable (&scr_showturtle);
487 Cvar_RegisterVariable (&scr_showpause);
488 Cvar_RegisterVariable (&scr_showfps);
489 Cvar_RegisterVariable (&scr_centertime);
490
491 Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
492 Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
493 Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
494
495 scr_initialized = true;
496 con_forcedup = true; // we're just initialized and not connected yet
497 }
498
499 //=============================================================================
500
501
502 /*
503 ==============
504 SCR_DrawRam
505 ==============
506 */
SCR_DrawRam(void)507 static void SCR_DrawRam (void)
508 {
509 if (!scr_showram.integer)
510 return;
511
512 if (!r_cache_thrash)
513 return;
514
515 Draw_Pic (scr_vrect.x+32, scr_vrect.y, scr_ram);
516 }
517
518 /*
519 ==============
520 SCR_DrawTurtle
521 ==============
522 */
SCR_DrawTurtle(void)523 static void SCR_DrawTurtle (void)
524 {
525 static int count;
526
527 if (!scr_showturtle.integer)
528 return;
529
530 if (host_frametime < HX_FRAME_TIME)
531 {
532 count = 0;
533 return;
534 }
535
536 count++;
537 if (count < 3)
538 return;
539
540 Draw_Pic (scr_vrect.x, scr_vrect.y, scr_turtle);
541 }
542
543 /*
544 ==============
545 SCR_DrawNet
546 ==============
547 */
SCR_DrawNet(void)548 static void SCR_DrawNet (void)
549 {
550 #if !defined(H2W)
551 if (realtime - cl.last_received_message < 0.3)
552 return;
553 #else
554 if (cls.netchan.outgoing_sequence -
555 cls.netchan.incoming_acknowledged < UPDATE_BACKUP-1)
556 return;
557 #endif
558 if (cls.demoplayback)
559 return;
560
561 Draw_Pic (scr_vrect.x+64, scr_vrect.y, scr_net);
562 }
563
SCR_DrawFPS(void)564 static void SCR_DrawFPS (void)
565 {
566 static double oldtime = 0;
567 static double lastfps = 0;
568 static int oldframecount = 0;
569 double elapsed_time;
570 int frames;
571
572 elapsed_time = realtime - oldtime;
573 frames = r_framecount - oldframecount;
574
575 if (elapsed_time < 0 || frames < 0)
576 {
577 oldtime = realtime;
578 oldframecount = r_framecount;
579 return;
580 }
581 // update value every 3/4 second
582 if (elapsed_time > 0.75)
583 {
584 lastfps = frames / elapsed_time;
585 oldtime = realtime;
586 oldframecount = r_framecount;
587 }
588
589 if (scr_showfps.integer)
590 {
591 char st[16];
592 int x, y;
593 sprintf(st, "%4.0f FPS", lastfps);
594 x = vid.width - strlen(st) * 8 - 8;
595 y = vid.height - sb_lines - 8;
596 // Draw_TileClear(x, y, strlen(st) * 8, 8);
597 Draw_String(x, y, st);
598 }
599 }
600
601 /*
602 ==============
603 DrawPause
604 ==============
605 */
SCR_DrawPause(void)606 static void SCR_DrawPause (void)
607 {
608 static qboolean newdraw = false;
609 static float LogoPercent, LogoTargetPercent;
610 qpic_t *pic;
611 int finaly;
612 float delta;
613
614 if (!scr_showpause.integer) // turn off for screenshots
615 return;
616
617 if (!cl.paused)
618 {
619 newdraw = false;
620 return;
621 }
622
623 if (!newdraw)
624 {
625 newdraw = true;
626 LogoTargetPercent = 1;
627 LogoPercent = 0;
628 }
629
630 pic = Draw_CachePic ("gfx/menu/paused.lmp");
631 // Draw_Pic ( (vid.width - pic->width)/2, (vid.height - 48 - pic->height)/2, pic);
632
633 if (LogoPercent < LogoTargetPercent)
634 {
635 delta = ((LogoTargetPercent - LogoPercent) / .5) * host_frametime;
636 if (delta < 0.004)
637 delta = 0.004;
638 LogoPercent += delta;
639 if (LogoPercent > LogoTargetPercent)
640 LogoPercent = LogoTargetPercent;
641 }
642
643 finaly = ((float)pic->height * LogoPercent) - pic->height;
644 Draw_TransPicCropped ( (vid.width - pic->width)/2, finaly, pic);
645 }
646
647 #if !defined(H2W)
648 /*
649 ==============
650 SCR_DrawLoading
651 ==============
652 */
653 #if !defined(DRAW_PROGRESSBARS)
SCR_DrawLoading(void)654 void SCR_DrawLoading (void)
655 {
656 int offset;
657 qpic_t *pic;
658
659 if (!scr_drawloading && loading_stage == 0)
660 return;
661
662 pic = Draw_CacheLoadingPic ();
663 offset = (vid.width - pic->width) / 2;
664 Draw_TransPic (offset, 0, pic);
665 }
666 #else
SCR_DrawLoading(void)667 void SCR_DrawLoading (void)
668 {
669 int size, count, offset;
670 qpic_t *pic;
671
672 if (!scr_drawloading && loading_stage == 0)
673 return;
674
675 pic = Draw_CachePic ("gfx/menu/loading.lmp");
676 offset = (vid.width - pic->width) / 2;
677 Draw_TransPic (offset, 0, pic);
678
679 if (loading_stage == 0)
680 return;
681
682 size = (total_loading_size) ?
683 (current_loading_size * 106 / total_loading_size) : 0;
684 offset += 42;
685
686 count = (loading_stage == 1) ? size : 106;
687 if (count)
688 {
689 Draw_Fill (offset, 87+0, count, 1, 136);
690 Draw_Fill (offset, 87+1, count, 4, 138);
691 Draw_Fill (offset, 87+5, count, 1, 136);
692 }
693
694 count = (loading_stage == 2) ? size : 0;
695 if (count)
696 {
697 Draw_Fill (offset, 97+0, count, 1, 168);
698 Draw_Fill (offset, 97+1, count, 4, 170);
699 Draw_Fill (offset, 97+5, count, 1, 168);
700 }
701 }
702 #endif /* !DRAW_PROGRESSBARS */
703
704 /*
705 ===============
706 SCR_BeginLoadingPlaque
707
708 ================
709 */
SCR_BeginLoadingPlaque(void)710 void SCR_BeginLoadingPlaque (void)
711 {
712 S_StopAllSounds (true);
713
714 if (cls.state != ca_connected)
715 return;
716 if (cls.signon != SIGNONS)
717 return;
718
719 // redraw with no console and the loading plaque
720 Con_ClearNotify ();
721 scr_centertime_off = 0;
722 scr_con_current = 0;
723
724 scr_drawloading = true;
725 scr_fullupdate = 0;
726 Sbar_Changed();
727 SCR_UpdateScreen ();
728 scr_drawloading = false;
729
730 scr_disabled_for_loading = true;
731 scr_disabled_time = realtime;
732 }
733
734 /*
735 ===============
736 SCR_EndLoadingPlaque
737
738 ================
739 */
SCR_EndLoadingPlaque(void)740 void SCR_EndLoadingPlaque (void)
741 {
742 scr_disabled_for_loading = false;
743 scr_fullupdate = 0;
744 scr_topupdate = 0;
745 Con_ClearNotify ();
746 }
747 #endif /* H2W */
748
749 //=============================================================================
750
751
752 /*
753 ==================
754 SCR_SetUpToDrawConsole
755 ==================
756 */
SCR_SetUpToDrawConsole(void)757 static void SCR_SetUpToDrawConsole (void)
758 {
759 Con_CheckResize ();
760
761 #if !defined(H2W)
762 if (scr_drawloading)
763 return; // never a console with loading plaque
764
765 con_forcedup = !cl.worldmodel || cls.signon != SIGNONS;
766 #else
767 con_forcedup = cls.state != ca_active;
768 #endif /* H2W */
769
770 // decide on the height of the console
771 if (con_forcedup)
772 {
773 scr_conlines = vid.height; // full screen
774 scr_con_current = scr_conlines;
775 }
776 else if (Key_GetDest() == key_console)
777 scr_conlines = vid.height / 2; // half screen
778 else
779 scr_conlines = 0; // none visible
780
781 if (scr_conlines < scr_con_current)
782 {
783 scr_con_current -= scr_conspeed.value * host_frametime;
784 if (scr_conlines > scr_con_current)
785 scr_con_current = scr_conlines;
786 }
787 else if (scr_conlines > scr_con_current)
788 {
789 scr_con_current += scr_conspeed.value * host_frametime;
790 if (scr_conlines < scr_con_current)
791 scr_con_current = scr_conlines;
792 }
793
794 if (clearconsole++ < vid.numpages)
795 {
796 scr_copytop = 1;
797 Draw_TileClear (0,(int)scr_con_current,vid.width, vid.height - (int)scr_con_current);
798 Sbar_Changed();
799 }
800 else if (clearnotify++ < vid.numpages)
801 {
802 scr_copytop = 1;
803 Draw_TileClear (0,0,vid.width, con_notifylines);
804 }
805 else
806 con_notifylines = 0;
807 }
808
809 /*
810 ==================
811 SCR_DrawConsole
812 ==================
813 */
SCR_DrawConsole(void)814 static void SCR_DrawConsole (void)
815 {
816 if (scr_con_current)
817 {
818 scr_copyeverything = 1;
819 Con_DrawConsole (scr_con_current);
820 clearconsole = 0;
821 }
822 else
823 {
824 keydest_t dest = Key_GetDest();
825 if (dest == key_game || dest == key_message)
826 Con_DrawNotify (); // only draw notify in game
827 }
828 }
829
830
831 /*
832 ==============================================================================
833
834 SCREEN SHOTS
835
836 ==============================================================================
837 */
838
839 #if !defined(H2W) /* FIXME!!! */
840 typedef struct
841 {
842 char manufacturer;
843 char version;
844 char encoding;
845 char bits_per_pixel;
846 unsigned short xmin,ymin,xmax,ymax;
847 unsigned short hres,vres;
848 unsigned char palette[48];
849 char reserved;
850 char color_planes;
851 unsigned short bytes_per_line;
852 unsigned short palette_type;
853 char filler[58];
854 unsigned char data; // unbounded
855 } pcx_t;
856 #endif /* H2W */
857
858 /*
859 ==============
860 WritePCXfile
861 ==============
862 */
WritePCXfile(const char * filename,byte * data,int width,int height,int rowbytes,byte * palette)863 static int WritePCXfile (const char *filename, byte *data, int width, int height, int rowbytes, byte *palette)
864 {
865 int i, j;
866 size_t length;
867 pcx_t *pcx;
868 byte *pack;
869
870 pcx = (pcx_t *) Hunk_TempAlloc (width*height*2+1000);
871 if (pcx == NULL)
872 {
873 Con_Printf("%s: not enough memory\n", __thisfunc__);
874 return -1;
875 }
876
877 pcx->manufacturer = 0x0a; // PCX id
878 pcx->version = 5; // 256 color
879 pcx->encoding = 1; // uncompressed
880 pcx->bits_per_pixel = 8; // 256 color
881 pcx->xmin = 0;
882 pcx->ymin = 0;
883 pcx->xmax = LittleShort((short)(width-1));
884 pcx->ymax = LittleShort((short)(height-1));
885 pcx->hres = LittleShort((short)width);
886 pcx->vres = LittleShort((short)height);
887 memset (pcx->palette, 0, sizeof(pcx->palette));
888 pcx->color_planes = 1; // chunky image
889 pcx->bytes_per_line = LittleShort((short)width);
890 pcx->palette_type = LittleShort(2); // not a grey scale
891 memset (pcx->filler, 0, sizeof(pcx->filler));
892
893 // pack the image
894 pack = &pcx->data;
895
896 for (i = 0; i < height; i++)
897 {
898 for (j = 0; j < width; j++)
899 {
900 if ( (*data & 0xc0) != 0xc0)
901 *pack++ = *data++;
902 else
903 {
904 *pack++ = 0xc1;
905 *pack++ = *data++;
906 }
907 }
908
909 data += rowbytes - width;
910 }
911
912 // write the palette
913 *pack++ = 0x0c; // palette ID byte
914 for (i = 0; i < 768; i++)
915 *pack++ = *palette++;
916
917 // write output file
918 length = pack - (byte *)pcx;
919 return FS_WriteFile(filename, pcx, length);
920 }
921
922 /*
923 ==================
924 SCR_ScreenShot_f
925 ==================
926 */
927 #if !defined(H2W)
928 static const char scr_shotbase[] = "shots/hexen00.pcx";
929 #define SHOTNUM_POS 11
930 #else
931 static const char scr_shotbase[] = "shots/hw00.pcx";
932 #define SHOTNUM_POS 8
933 #endif
SCR_ScreenShot_f(void)934 static void SCR_ScreenShot_f (void)
935 {
936 char pcxname[80];
937 char checkname[MAX_OSPATH];
938 int i;
939
940 FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), "shots");
941 Sys_mkdir (checkname, false);
942 // find a slot to save it to
943 q_strlcpy (pcxname, scr_shotbase, sizeof(pcxname));
944 for (i = 0; i <= 99; i++)
945 {
946 pcxname[SHOTNUM_POS+0] = i/10 + '0';
947 pcxname[SHOTNUM_POS+1] = i%10 + '0';
948 FS_MakePath_BUF (FS_USERDIR, NULL, checkname, sizeof(checkname), pcxname);
949 if (Sys_FileType(checkname) == FS_ENT_NONE)
950 break; // file doesn't exist
951 }
952 if (i == 100)
953 {
954 Con_Printf ("%s: Couldn't create a PCX file\n", __thisfunc__);
955 return;
956 }
957
958 //
959 // save the pcx file
960 //
961 D_EnableBackBufferAccess (); // enable direct drawing of console
962 // to back buffer
963
964 i = WritePCXfile (pcxname, vid.buffer, vid.width, vid.height, vid.rowbytes, host_basepal);
965
966 D_DisableBackBufferAccess (); // for adapters that can't stay mapped
967 // in for linear writes all the time
968
969 if (i == 0)
970 Con_Printf ("Wrote %s\n", pcxname);
971 }
972
973 //=============================================================================
974
975
976 static const char *scr_notifystring;
977 static qboolean scr_drawdialog;
978
SCR_DrawNotifyString(void)979 static void SCR_DrawNotifyString (void)
980 {
981 Plaque_Draw(scr_notifystring, true);
982 }
983
984 /*
985 ==================
986 SCR_ModalMessage
987
988 Displays a text string in the center of the screen
989 and waits for a Y or N keypress.
990 ==================
991 */
SCR_ModalMessage(const char * text)992 int SCR_ModalMessage (const char *text)
993 {
994 #if !defined(H2W)
995 if (cls.state == ca_dedicated)
996 return true;
997 #endif /* H2W */
998 scr_notifystring = text;
999
1000 // draw a fresh screen
1001 scr_fullupdate = 0;
1002 scr_drawdialog = true;
1003 SCR_UpdateScreen ();
1004 scr_drawdialog = false;
1005
1006 S_ClearBuffer (); // so dma doesn't loop current sound
1007
1008 do
1009 {
1010 key_count = -1; // wait for a key down and up
1011 Sys_SendKeyEvents ();
1012 } while (key_lastpress != 'y' && key_lastpress != 'n' && key_lastpress != K_ESCAPE);
1013
1014 scr_fullupdate = 0;
1015 SCR_UpdateScreen ();
1016
1017 return key_lastpress == 'y';
1018 }
1019
1020 //=============================================================================
1021
1022 /*
1023 ===============
1024 SCR_BringDownConsole
1025
1026 Brings the console down and fades the palettes back to normal
1027 ================
1028 */
1029 #if 0 /* all uses are commented out */
1030 void SCR_BringDownConsole (void)
1031 {
1032 int i;
1033
1034 scr_centertime_off = 0;
1035
1036 for (i = 0; i < 20 && scr_conlines != scr_con_current; i++)
1037 SCR_UpdateScreen ();
1038
1039 cl.cshifts[0].percent = 0; // no area contents palette on next frame
1040 VID_SetPalette (host_basepal);
1041 }
1042 #endif
1043
1044 //=============================================================================
1045
SCR_SetPlaqueMessage(const char * msg)1046 void SCR_SetPlaqueMessage (const char *msg)
1047 {
1048 plaquemessage = msg;
1049 }
1050
Plaque_Draw(const char * message,qboolean AlwaysDraw)1051 static void Plaque_Draw (const char *message, qboolean AlwaysDraw)
1052 {
1053 int i, cnt;
1054 int bx, by;
1055 char temp[80];
1056
1057 if (scr_con_current == vid.height && !AlwaysDraw)
1058 return; // console is full screen
1059
1060 if (!*message)
1061 return;
1062
1063 scr_needfull = true;
1064
1065 FindTextBreaks(message, PLAQUE_WIDTH);
1066
1067 by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1);
1068 M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2);
1069
1070 for (i = 0; i < lines; i++, by += 8)
1071 {
1072 cnt = EndC[i] - StartC[i];
1073 strncpy (temp, &message[StartC[i]], cnt);
1074 temp[cnt] = 0;
1075 bx = (40-strlen(temp)) * 8 / 2;
1076 M_Print (bx, by, temp);
1077 }
1078 }
1079
1080 #if !defined(H2W)
Info_Plaque_Draw(const char * message)1081 static void Info_Plaque_Draw (const char *message)
1082 {
1083 int i, cnt;
1084 int bx, by;
1085 char temp[80];
1086
1087 if (scr_con_current == vid.height)
1088 return; // console is full screen
1089
1090 if (!info_string_count || !*message)
1091 return;
1092
1093 scr_needfull = true;
1094
1095 FindTextBreaks(message, PLAQUE_WIDTH+4);
1096
1097 if (lines == MAXLINES)
1098 {
1099 Con_DPrintf("%s: line overflow error\n", __thisfunc__);
1100 lines = MAXLINES-1;
1101 }
1102
1103 by = (25-lines) * 8 / 2 + ((vid.height - 200)>>1);
1104 M_DrawTextBox (15, by - 16, PLAQUE_WIDTH + 4 + 4, lines + 2);
1105
1106 for (i = 0; i < lines; i++, by += 8)
1107 {
1108 cnt = EndC[i] - StartC[i];
1109 strncpy (temp, &message[StartC[i]], cnt);
1110 temp[cnt] = 0;
1111 bx = (40-strlen(temp)) * 8 / 2;
1112 M_Print (bx, by, temp);
1113 }
1114 }
1115
Bottom_Plaque_Draw(const char * message)1116 static void Bottom_Plaque_Draw (const char *message)
1117 {
1118 int i, cnt;
1119 int bx, by;
1120 char temp[80];
1121
1122 if (!*message)
1123 return;
1124
1125 scr_needfull = true;
1126
1127 FindTextBreaks(message, PLAQUE_WIDTH);
1128
1129 by = (((vid.height) / 8) - lines - 2) * 8;
1130 M_DrawTextBox (32, by - 16, PLAQUE_WIDTH + 4, lines + 2);
1131
1132 for (i = 0; i < lines; i++, by += 8)
1133 {
1134 cnt = EndC[i] - StartC[i];
1135 strncpy (temp, &message[StartC[i]], cnt);
1136 temp[cnt] = 0;
1137 bx = (40-strlen(temp)) * 8 / 2;
1138 M_Print (bx, by, temp);
1139 }
1140 }
1141
1142 //=============================================================================
1143
1144
I_Print(int cx,int cy,const char * str,int flags)1145 static void I_Print (int cx, int cy, const char *str, int flags)
1146 {
1147 int num, x, y;
1148 const char *s;
1149
1150 x = cx + ((vid.width - 320)>>1);
1151 y = cy;
1152 if (!(flags & (INTERMISSION_PRINT_TOP|INTERMISSION_PRINT_TOPMOST)))
1153 y += ((vid.height - 200)>>1);
1154 s = str;
1155
1156 while (*s)
1157 {
1158 num = (unsigned char)(*s);
1159 if (!(flags & INTERMISSION_PRINT_WHITE))
1160 num += 256;
1161 Draw_Character (x, y, num);
1162 s++;
1163 x += 8;
1164 }
1165 }
1166
1167 #if FULLSCREEN_INTERMISSIONS
1168 # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePicResize((X),(Y),(Z))
1169 # define Draw_IntermissionPic_FN(X,Y,Z) Draw_Pic(0,0,(Z))
1170 #else
1171 # define Load_IntermissionPic_FN(X,Y,Z) Draw_CachePic((X))
1172 # define Draw_IntermissionPic_FN(X,Y,Z) Draw_Pic((X),(Y),(Z))
1173 #endif
1174
1175 /*
1176 ===============
1177 SB_IntermissionOverlay
1178 ===============
1179 */
SB_IntermissionOverlay(void)1180 static void SB_IntermissionOverlay (void)
1181 {
1182 qpic_t *pic;
1183 int elapsed, size, bx, by, i;
1184 char temp[80];
1185 const char *message;
1186
1187 scr_copyeverything = 1;
1188 scr_fullupdate = 0;
1189
1190 #if !defined(H2W)
1191 if (cl.gametype == GAME_DEATHMATCH)
1192 #else
1193 if (!cl_siege)
1194 #endif
1195 {
1196 Sbar_DeathmatchOverlay ();
1197 return;
1198 }
1199
1200 if (cl.intermission_pic == NULL)
1201 Host_Error ("%s: NULL intermission picture", __thisfunc__);
1202 else
1203 {
1204 pic = Load_IntermissionPic_FN (cl.intermission_pic, vid.width, vid.height);
1205 Draw_IntermissionPic_FN (((vid.width - 320)>>1), ((vid.height - 200)>>1), pic);
1206 }
1207
1208 if (cl.message_index >= 0 && cl.message_index < host_string_count)
1209 message = Host_GetString (cl.message_index);
1210 else if (cl.intermission_flags & INTERMISSION_NO_MESSAGE)
1211 message = "";
1212 else
1213 {
1214 message = ""; /* silence compilers */
1215 Host_Error ("%s: Intermission string #%d not available (host_string_count: %d)",
1216 __thisfunc__, cl.message_index, host_string_count);
1217 }
1218
1219 if (cl.intermission_flags & INTERMISSION_NOT_CONNECTED)
1220 elapsed = (realtime - cl.completed_time) * 20;
1221 else elapsed = (cl.time - cl.completed_time) * 20;
1222 if (cl.intermission_flags & INTERMISSION_PRINT_DELAY)
1223 {
1224 elapsed -= 50; /* delay about 2.5 seconds */
1225 if (elapsed < 0)
1226 elapsed = 0;
1227 }
1228
1229 FindTextBreaks(message, 38);
1230
1231 if (cl.intermission_flags & INTERMISSION_PRINT_TOPMOST)
1232 by = 16;
1233 else by = (25-lines) * 8 / 2;
1234
1235 for (i = 0; i < lines; i++, by += 8)
1236 {
1237 size = EndC[i] - StartC[i];
1238 strncpy (temp, &message[StartC[i]], size);
1239
1240 if (size > elapsed)
1241 size = elapsed;
1242 temp[size] = 0;
1243
1244 bx = (40-strlen(temp)) * 8 / 2;
1245 I_Print (bx, by, temp, cl.intermission_flags);
1246
1247 elapsed -= size;
1248 if (elapsed <= 0)
1249 break;
1250 }
1251
1252 if (i == lines && cl.lasting_time && elapsed >= 20*cl.lasting_time)
1253 CL_SetupIntermission (cl.intermission_next);
1254 }
1255 #endif /* H2W */
1256
1257 //=============================================================================
1258
1259
1260 /*
1261 ==================
1262 SCR_UpdateScreen
1263
1264 This is called every frame, and can also be called explicitly to flush
1265 text to the screen.
1266
1267 WARNING: be very careful calling this from elsewhere, because the refresh
1268 needs almost the entire 256k of stack space!
1269 ==================
1270 */
SCR_UpdateScreen(void)1271 void SCR_UpdateScreen (void)
1272 {
1273 vrect_t vrect;
1274
1275 if (scr_skipupdate || block_drawing)
1276 return;
1277
1278 #ifdef PLATFORM_WINDOWS
1279 // don't suck up any cpu if minimized
1280 if (Minimized)
1281 return;
1282 #endif
1283
1284 scr_copytop = 0;
1285 scr_copyeverything = 0;
1286
1287 #if defined(H2W)
1288 if (scr_disabled_for_loading)
1289 return;
1290 #else
1291 if (scr_disabled_for_loading)
1292 {
1293 if (realtime - scr_disabled_time > 25) {
1294 /* this can happen with clients connected to servers
1295 * older than uHexen2-1.5.6 who don't issue an error
1296 * upon changelevel failures. Or, it could happen if
1297 * loading is taking a really long time.
1298 */
1299 scr_disabled_for_loading = false;
1300 total_loading_size = 0;
1301 loading_stage = 0;
1302 Con_Printf ("load timeout.\n");
1303 }
1304 else {
1305 return;
1306 }
1307 }
1308
1309 if (cls.state == ca_dedicated)
1310 return; // stdout only
1311 #endif /* H2W */
1312
1313 if (!scr_initialized || !con_initialized)
1314 return; // not initialized yet
1315
1316 //
1317 // check for vid changes
1318 //
1319 if (vid.recalc_refdef)
1320 {
1321 // something changed, so reorder the screen
1322 SCR_CalcRefdef ();
1323 }
1324
1325 //
1326 // do 3D refresh drawing, and then update the screen
1327 //
1328 D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly
1329
1330 if (scr_needfull && (!plaquemessage || !*plaquemessage || !SCR_CheckDrawCenterString2()))
1331 scr_fullupdate = 0;
1332
1333 if (scr_fullupdate++ < vid.numpages)
1334 { // clear the entire screen
1335 scr_copyeverything = 1;
1336 Draw_TileClear (0,0,vid.width,vid.height);
1337 Sbar_Changed();
1338 }
1339 else if (scr_topupdate++ < vid.numpages)
1340 {
1341 scr_copyeverything = 1;
1342 Draw_TileClear (0,0,vid.width,100);
1343 Sbar_Changed();
1344 }
1345
1346 // pconupdate = NULL;
1347
1348 SCR_SetUpToDrawConsole ();
1349 // SCR_EraseCenterString ();
1350
1351 D_DisableBackBufferAccess (); // for adapters that can't stay mapped
1352 // in for linear writes all the time
1353
1354 #if FULLSCREEN_INTERMISSIONS
1355 // no need to draw view in fullscreen intermission screens
1356 if (!cl.intermission)
1357 #endif
1358 {
1359 VID_LockBuffer ();
1360 V_RenderView ();
1361 VID_UnlockBuffer ();
1362 }
1363
1364 D_EnableBackBufferAccess (); // of all overlay stuff if drawing directly
1365
1366 if (scr_drawdialog)
1367 {
1368 Sbar_Draw ();
1369 Draw_FadeScreen ();
1370 SCR_DrawNotifyString ();
1371 scr_copyeverything = true;
1372 }
1373 else if (cl.intermission)
1374 {
1375 #if !defined(H2W)
1376 SB_IntermissionOverlay();
1377 if (!(cl.intermission_flags & INTERMISSION_NO_MENUS))
1378 {
1379 SCR_DrawConsole();
1380 M_Draw();
1381 }
1382
1383 if (scr_drawloading)
1384 SCR_DrawLoading();
1385 #endif /* H2W */
1386 }
1387 #if !defined(H2W)
1388 else if (scr_drawloading)
1389 {
1390 Draw_FadeScreen ();
1391 SCR_DrawLoading ();
1392 }
1393 #endif /* H2W */
1394 else
1395 {
1396 if (crosshair.integer && !cls.demoplayback)
1397 Draw_Crosshair();
1398
1399 SCR_DrawRam();
1400 SCR_DrawNet();
1401 SCR_DrawTurtle();
1402 SCR_DrawPause();
1403 SCR_CheckDrawCenterString();
1404 Sbar_Draw();
1405 SCR_DrawFPS();
1406
1407 Plaque_Draw(plaquemessage, false);
1408 SCR_DrawConsole();
1409 M_Draw();
1410
1411 #if !defined(H2W)
1412 if (info_up)
1413 {
1414 UpdateInfoMessage();
1415 Info_Plaque_Draw(infomessage);
1416 }
1417 #endif /* H2W */
1418 }
1419
1420 D_DisableBackBufferAccess (); // for adapters that can't stay mapped
1421 // in for linear writes all the time
1422 //if (pconupdate)
1423 // D_UpdateRects (pconupdate);
1424
1425 V_UpdatePalette ();
1426
1427 //
1428 // update one of three areas
1429 //
1430 if (scr_copyeverything)
1431 {
1432 vrect.x = 0;
1433 vrect.y = 0;
1434 vrect.width = vid.width;
1435 vrect.height = vid.height;
1436 vrect.pnext = NULL;
1437
1438 VID_Update (&vrect);
1439 }
1440 else if (scr_copytop)
1441 {
1442 vrect.x = 0;
1443 vrect.y = 0;
1444 vrect.width = vid.width;
1445 vrect.height = vid.height - sb_lines;
1446 vrect.pnext = NULL;
1447
1448 VID_Update (&vrect);
1449 }
1450 else
1451 {
1452 vrect.x = scr_vrect.x;
1453 vrect.y = scr_vrect.y;
1454 vrect.width = scr_vrect.width;
1455 vrect.height = scr_vrect.height;
1456 vrect.pnext = NULL;
1457
1458 VID_Update (&vrect);
1459 }
1460 }
1461
1462
1463 /*
1464 ==================
1465 SCR_UpdateWholeScreen
1466 ==================
1467 */
SCR_UpdateWholeScreen(void)1468 void SCR_UpdateWholeScreen (void)
1469 {
1470 scr_fullupdate = 0;
1471 SCR_UpdateScreen ();
1472 }
1473
1474