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