1 /*
2 ** shared_sbar.cpp
3 ** Base status bar implementation
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include <assert.h>
36 
37 #include "templates.h"
38 #include "sbar.h"
39 #include "c_cvars.h"
40 #include "c_dispatch.h"
41 #include "c_console.h"
42 #include "v_video.h"
43 #include "m_swap.h"
44 #include "w_wad.h"
45 #include "v_text.h"
46 #include "s_sound.h"
47 #include "gi.h"
48 #include "doomstat.h"
49 #include "g_level.h"
50 #include "d_net.h"
51 #include "colormatcher.h"
52 #include "v_palette.h"
53 #include "d_player.h"
54 #include "farchive.h"
55 #include "a_hexenglobal.h"
56 #include "gstrings.h"
57 
58 #include "../version.h"
59 
60 #define XHAIRSHRINKSIZE		(FRACUNIT/18)
61 #define XHAIRPICKUPSIZE		(FRACUNIT*2+XHAIRSHRINKSIZE)
62 #define POWERUPICONSIZE		32
63 
64 IMPLEMENT_POINTY_CLASS(DBaseStatusBar)
65 	DECLARE_POINTER(Messages[0])
66 	DECLARE_POINTER(Messages[1])
67 	DECLARE_POINTER(Messages[2])
68 END_POINTERS
69 
70 EXTERN_CVAR (Bool, am_showmonsters)
71 EXTERN_CVAR (Bool, am_showsecrets)
72 EXTERN_CVAR (Bool, am_showitems)
73 EXTERN_CVAR (Bool, am_showtime)
74 EXTERN_CVAR (Bool, am_showtotaltime)
75 EXTERN_CVAR (Bool, noisedebug)
76 EXTERN_CVAR (Int, con_scaletext)
77 
78 DBaseStatusBar *StatusBar;
79 
80 extern int setblocks;
81 
82 int ST_X, ST_Y;
83 int SB_state = 3;
84 
85 FTexture *CrosshairImage;
86 static int CrosshairNum;
87 
88 // [RH] Base blending values (for e.g. underwater)
89 int BaseBlendR, BaseBlendG, BaseBlendB;
90 float BaseBlendA;
91 
92 CVAR (Int, paletteflash, 0, CVAR_ARCHIVE)
CVAR(Flag,pf_hexenweaps,paletteflash,PF_HEXENWEAPONS)93 CVAR (Flag, pf_hexenweaps,	paletteflash, PF_HEXENWEAPONS)
94 CVAR (Flag, pf_poison,		paletteflash, PF_POISON)
95 CVAR (Flag, pf_ice,			paletteflash, PF_ICE)
96 CVAR (Flag, pf_hazard,		paletteflash, PF_HAZARD)
97 
98 // Stretch status bar to full screen width?
99 CUSTOM_CVAR (Bool, st_scale, true, CVAR_ARCHIVE)
100 {
101 	if (StatusBar)
102 	{
103 		StatusBar->SetScaled (self);
104 		setsizeneeded = true;
105 	}
106 }
107 
108 CVAR (Int, crosshair, 0, CVAR_ARCHIVE)
109 CVAR (Bool, crosshairforce, false, CVAR_ARCHIVE)
110 CVAR (Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE);
111 CVAR (Bool, crosshairhealth, true, CVAR_ARCHIVE);
112 CVAR (Bool, crosshairscale, false, CVAR_ARCHIVE);
113 CVAR (Bool, crosshairgrow, false, CVAR_ARCHIVE);
114 CUSTOM_CVAR(Int, am_showmaplabel, 2, CVAR_ARCHIVE)
115 {
116 	if (self < 0 || self > 2) self = 2;
117 }
118 
119 CVAR (Bool, idmypos, false, 0);
120 
121 //---------------------------------------------------------------------------
122 //
123 // Format the map name, include the map label if wanted
124 //
125 //---------------------------------------------------------------------------
126 
ST_FormatMapName(FString & mapname,const char * mapnamecolor)127 void ST_FormatMapName(FString &mapname, const char *mapnamecolor)
128 {
129 	cluster_info_t *cluster = FindClusterInfo (level.cluster);
130 	bool ishub = (cluster != NULL && (cluster->flags & CLUSTER_HUB));
131 
132 	if (am_showmaplabel == 1 || (am_showmaplabel == 2 && !ishub))
133 	{
134 		mapname << level.MapName << ": ";
135 	}
136 	mapname << mapnamecolor << level.LevelName;
137 }
138 
139 //---------------------------------------------------------------------------
140 //
141 // Load crosshair definitions
142 //
143 //---------------------------------------------------------------------------
144 
ST_LoadCrosshair(bool alwaysload)145 void ST_LoadCrosshair(bool alwaysload)
146 {
147 	int num = 0;
148 	char name[16], size;
149 
150 	if (!crosshairforce &&
151 		players[consoleplayer].camera != NULL &&
152 		players[consoleplayer].camera->player != NULL &&
153 		players[consoleplayer].camera->player->ReadyWeapon != NULL)
154 	{
155 		num = players[consoleplayer].camera->player->ReadyWeapon->Crosshair;
156 	}
157 	if (num == 0)
158 	{
159 		num = crosshair;
160 	}
161 	if (!alwaysload && CrosshairNum == num && CrosshairImage != NULL)
162 	{ // No change.
163 		return;
164 	}
165 
166 	if (CrosshairImage != NULL)
167 	{
168 		CrosshairImage->Unload ();
169 	}
170 	if (num == 0)
171 	{
172 		CrosshairNum = 0;
173 		CrosshairImage = NULL;
174 		return;
175 	}
176 	if (num < 0)
177 	{
178 		num = -num;
179 	}
180 	size = (SCREENWIDTH < 640) ? 'S' : 'B';
181 
182 	mysnprintf (name, countof(name), "XHAIR%c%d", size, num);
183 	FTextureID texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
184 	if (!texid.isValid())
185 	{
186 		mysnprintf (name, countof(name), "XHAIR%c1", size);
187 		texid = TexMan.CheckForTexture(name, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
188 		if (!texid.isValid())
189 		{
190 			texid = TexMan.CheckForTexture("XHAIRS1", FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_ShortNameOnly);
191 		}
192 	}
193 	CrosshairNum = num;
194 	CrosshairImage = TexMan[texid];
195 }
196 
197 //---------------------------------------------------------------------------
198 //
199 // ST_Clear
200 //
201 //---------------------------------------------------------------------------
202 
ST_Clear()203 void ST_Clear()
204 {
205 	if (StatusBar != NULL)
206 	{
207 		StatusBar->Destroy();
208 		StatusBar = NULL;
209 	}
210 	CrosshairImage = NULL;
211 	CrosshairNum = 0;
212 }
213 
214 //---------------------------------------------------------------------------
215 //
216 // ST_SetNeedRefresh
217 //
218 //---------------------------------------------------------------------------
219 
ST_SetNeedRefresh()220 void ST_SetNeedRefresh()
221 {
222 	SB_state = (StatusBar == NULL || screen == NULL) ? 0 : screen->GetPageCount();
223 }
224 
225 //---------------------------------------------------------------------------
226 //
227 // Constructor
228 //
229 //---------------------------------------------------------------------------
230 
DBaseStatusBar(int reltop,int hres,int vres)231 DBaseStatusBar::DBaseStatusBar (int reltop, int hres, int vres)
232 {
233 	CompleteBorder = false;
234 	Centering = false;
235 	FixedOrigin = false;
236 	CrosshairSize = FRACUNIT;
237 	RelTop = reltop;
238 	memset(Messages, 0, sizeof(Messages));
239 	Displacement = 0;
240 	CPlayer = NULL;
241 	ShowLog = false;
242 	HorizontalResolution = hres;
243 	VirticalResolution = vres;
244 
245 	SetScaled (st_scale);
246 }
247 
248 //---------------------------------------------------------------------------
249 //
250 // PROP Destroy
251 //
252 //---------------------------------------------------------------------------
253 
Destroy()254 void DBaseStatusBar::Destroy ()
255 {
256 	for (unsigned int i = 0; i < countof(Messages); ++i)
257 	{
258 		DHUDMessage *msg = Messages[i];
259 		while (msg)
260 		{
261 			DHUDMessage *next = msg->Next;
262 			msg->Destroy();
263 			msg = next;
264 		}
265 		Messages[i] = NULL;
266 	}
267 	Super::Destroy();
268 }
269 
270 //---------------------------------------------------------------------------
271 //
272 // PROC SetScaled
273 //
274 //---------------------------------------------------------------------------
275 
276 //[BL] Added force argument to have forcescaled mean forcescaled.
277 // - Also, if the VirticalResolution is something other than the default (200)
278 //   We should always obey the value of scale.
SetScaled(bool scale,bool force)279 void DBaseStatusBar::SetScaled (bool scale, bool force)
280 {
281 	Scaled = (RelTop != 0 || force) && ((SCREENWIDTH != 320 || HorizontalResolution != 320) && scale);
282 
283 	if (!Scaled)
284 	{
285 		ST_X = (SCREENWIDTH - HorizontalResolution) / 2;
286 		ST_Y = SCREENHEIGHT - RelTop;
287 		::ST_Y = ST_Y;
288 		if (RelTop > 0)
289 		{
290 			Displacement = ((ST_Y * VirticalResolution / SCREENHEIGHT) - (VirticalResolution - RelTop))*FRACUNIT/RelTop;
291 		}
292 		else
293 		{
294 			Displacement = 0;
295 		}
296 	}
297 	else
298 	{
299 		ST_X = 0;
300 		ST_Y = VirticalResolution - RelTop;
301 		if (CheckRatio(SCREENWIDTH, SCREENHEIGHT) != 4)
302 		{ // Normal resolution
303 			::ST_Y = Scale (ST_Y, SCREENHEIGHT, VirticalResolution);
304 		}
305 		else
306 		{ // 5:4 resolution
307 			::ST_Y = Scale(ST_Y - VirticalResolution/2, SCREENHEIGHT*3, Scale(VirticalResolution, BaseRatioSizes[4][1], 200)) + SCREENHEIGHT/2
308 				+ (SCREENHEIGHT - SCREENHEIGHT * BaseRatioSizes[4][3] / 48) / 2;
309 		}
310 		Displacement = 0;
311 	}
312 	::ST_X = ST_X;
313 	ST_SetNeedRefresh();
314 }
315 
316 //---------------------------------------------------------------------------
317 //
318 // PROC AttachToPlayer
319 //
320 //---------------------------------------------------------------------------
321 
AttachToPlayer(player_t * player)322 void DBaseStatusBar::AttachToPlayer (player_t *player)
323 {
324 	CPlayer = player;
325 	ST_SetNeedRefresh();
326 }
327 
328 //---------------------------------------------------------------------------
329 //
330 // PROC GetPlayer
331 //
332 //---------------------------------------------------------------------------
333 
GetPlayer()334 int DBaseStatusBar::GetPlayer ()
335 {
336 	return int(CPlayer - players);
337 }
338 
339 //---------------------------------------------------------------------------
340 //
341 // PROC MultiplayerChanged
342 //
343 //---------------------------------------------------------------------------
344 
MultiplayerChanged()345 void DBaseStatusBar::MultiplayerChanged ()
346 {
347 	ST_SetNeedRefresh();
348 }
349 
350 //---------------------------------------------------------------------------
351 //
352 // PROC Tick
353 //
354 //---------------------------------------------------------------------------
355 
Tick()356 void DBaseStatusBar::Tick ()
357 {
358 	for (unsigned int i = 0; i < countof(Messages); ++i)
359 	{
360 		DHUDMessage *msg = Messages[i];
361 		DHUDMessage **prev = &Messages[i];
362 
363 		while (msg)
364 		{
365 			DHUDMessage *next = msg->Next;
366 
367 			if (msg->Tick ())
368 			{
369 				*prev = next;
370 				msg->Destroy();
371 			}
372 			else
373 			{
374 				prev = &msg->Next;
375 			}
376 			msg = next;
377 		}
378 
379 		// If the crosshair has been enlarged, shrink it.
380 		if (CrosshairSize > FRACUNIT)
381 		{
382 			CrosshairSize -= XHAIRSHRINKSIZE;
383 			if (CrosshairSize < FRACUNIT)
384 			{
385 				CrosshairSize = FRACUNIT;
386 			}
387 		}
388 	}
389 }
390 
391 //---------------------------------------------------------------------------
392 //
393 // PROC AttachMessage
394 //
395 //---------------------------------------------------------------------------
396 
AttachMessage(DHUDMessage * msg,DWORD id,int layer)397 void DBaseStatusBar::AttachMessage (DHUDMessage *msg, DWORD id, int layer)
398 {
399 	DHUDMessage *old = NULL;
400 	DHUDMessage **prev;
401 	DObject *container = this;
402 
403 	old = (id == 0 || id == 0xFFFFFFFF) ? NULL : DetachMessage (id);
404 	if (old != NULL)
405 	{
406 		old->Destroy();
407 	}
408 
409 	// Merge unknown layers into the default layer.
410 	if ((size_t)layer >= countof(Messages))
411 	{
412 		layer = HUDMSGLayer_Default;
413 	}
414 
415 	prev = &Messages[layer];
416 
417 	// The ID serves as a priority, where lower numbers appear in front of
418 	// higher numbers. (i.e. The list is sorted in descending order, since
419 	// it gets drawn back to front.)
420 	while (*prev != NULL && (*prev)->SBarID > id)
421 	{
422 		container = *prev;
423 		prev = &(*prev)->Next;
424 	}
425 
426 	msg->Next = *prev;
427 	msg->SBarID = id;
428 	*prev = msg;
429 	GC::WriteBarrier(container, msg);
430 }
431 
432 //---------------------------------------------------------------------------
433 //
434 // PROC DetachMessage
435 //
436 //---------------------------------------------------------------------------
437 
DetachMessage(DHUDMessage * msg)438 DHUDMessage *DBaseStatusBar::DetachMessage (DHUDMessage *msg)
439 {
440 	for (unsigned int i = 0; i < countof(Messages); ++i)
441 	{
442 		DHUDMessage *probe = Messages[i];
443 		DHUDMessage **prev = &Messages[i];
444 
445 		while (probe && probe != msg)
446 		{
447 			prev = &probe->Next;
448 			probe = probe->Next;
449 		}
450 		if (probe != NULL)
451 		{
452 			*prev = probe->Next;
453 			probe->Next = NULL;
454 			// Redraw the status bar in case it was covered
455 			if (screen != NULL)
456 			{
457 				ST_SetNeedRefresh();
458 			}
459 			return probe;
460 		}
461 	}
462 	return NULL;
463 }
464 
DetachMessage(DWORD id)465 DHUDMessage *DBaseStatusBar::DetachMessage (DWORD id)
466 {
467 	for (unsigned int i = 0; i < countof(Messages); ++i)
468 	{
469 		DHUDMessage *probe = Messages[i];
470 		DHUDMessage **prev = &Messages[i];
471 
472 		while (probe && probe->SBarID != id)
473 		{
474 			prev = &probe->Next;
475 			probe = probe->Next;
476 		}
477 		if (probe != NULL)
478 		{
479 			*prev = probe->Next;
480 			probe->Next = NULL;
481 			// Redraw the status bar in case it was covered
482 			if (screen != NULL)
483 			{
484 				ST_SetNeedRefresh();
485 			}
486 			return probe;
487 		}
488 	}
489 	return NULL;
490 }
491 
492 //---------------------------------------------------------------------------
493 //
494 // PROC DetachAllMessages
495 //
496 //---------------------------------------------------------------------------
497 
DetachAllMessages()498 void DBaseStatusBar::DetachAllMessages ()
499 {
500 	for (unsigned int i = 0; i < countof(Messages); ++i)
501 	{
502 		DHUDMessage *probe = Messages[i];
503 
504 		Messages[i] = NULL;
505 		while (probe != NULL)
506 		{
507 			DHUDMessage *next = probe->Next;
508 			probe->Destroy();
509 			probe = next;
510 		}
511 	}
512 }
513 
514 //---------------------------------------------------------------------------
515 //
516 // PROC ShowPlayerName
517 //
518 //---------------------------------------------------------------------------
519 
ShowPlayerName()520 void DBaseStatusBar::ShowPlayerName ()
521 {
522 	EColorRange color;
523 
524 	color = (CPlayer == &players[consoleplayer]) ? CR_GOLD : CR_GREEN;
525 	AttachMessage (new DHUDMessageFadeOut (SmallFont, CPlayer->userinfo.GetName(),
526 		1.5f, 0.92f, 0, 0, color, 2.f, 0.35f), MAKE_ID('P','N','A','M'));
527 }
528 
529 //---------------------------------------------------------------------------
530 //
531 // PROC DrawImage
532 //
533 // Draws an image with the status bar's upper-left corner as the origin.
534 //
535 //---------------------------------------------------------------------------
536 
DrawImage(FTexture * img,int x,int y,FRemapTable * translation) const537 void DBaseStatusBar::DrawImage (FTexture *img,
538 	int x, int y, FRemapTable *translation) const
539 {
540 	if (img != NULL)
541 	{
542 		screen->DrawTexture (img, x + ST_X, y + ST_Y,
543 			DTA_Translation, translation,
544 			DTA_Bottom320x200, Scaled,
545 			TAG_DONE);
546 	}
547 }
548 
549 //---------------------------------------------------------------------------
550 //
551 // PROC DrawImage
552 //
553 // Draws an optionally dimmed image with the status bar's upper-left corner
554 // as the origin.
555 //
556 //---------------------------------------------------------------------------
557 
DrawDimImage(FTexture * img,int x,int y,bool dimmed) const558 void DBaseStatusBar::DrawDimImage (FTexture *img,
559 	int x, int y, bool dimmed) const
560 {
561 	if (img != NULL)
562 	{
563 		screen->DrawTexture (img, x + ST_X, y + ST_Y,
564 			DTA_ColorOverlay, dimmed ? DIM_OVERLAY : 0,
565 			DTA_Bottom320x200, Scaled,
566 			TAG_DONE);
567 	}
568 }
569 
570 //---------------------------------------------------------------------------
571 //
572 // PROC DrawImage
573 //
574 // Draws a translucent image with the status bar's upper-left corner as the
575 // origin.
576 //
577 //---------------------------------------------------------------------------
578 
DrawFadedImage(FTexture * img,int x,int y,fixed_t shade) const579 void DBaseStatusBar::DrawFadedImage (FTexture *img,
580 	int x, int y, fixed_t shade) const
581 {
582 	if (img != NULL)
583 	{
584 		screen->DrawTexture (img, x + ST_X, y + ST_Y,
585 			DTA_Alpha, shade,
586 			DTA_Bottom320x200, Scaled,
587 			TAG_DONE);
588 	}
589 }
590 
591 //---------------------------------------------------------------------------
592 //
593 // PROC DrawPartialImage
594 //
595 // Draws a portion of an image with the status bar's upper-left corner as
596 // the origin. The image should be the same size as the status bar.
597 // Used for Doom's status bar.
598 //
599 //---------------------------------------------------------------------------
600 
DrawPartialImage(FTexture * img,int wx,int ww) const601 void DBaseStatusBar::DrawPartialImage (FTexture *img, int wx, int ww) const
602 {
603 	if (img != NULL)
604 	{
605 		screen->DrawTexture (img, ST_X, ST_Y,
606 			DTA_WindowLeft, wx,
607 			DTA_WindowRight, wx + ww,
608 			DTA_Bottom320x200, Scaled,
609 			TAG_DONE);
610 	}
611 }
612 
613 //---------------------------------------------------------------------------
614 //
615 // PROC DrINumber
616 //
617 // Draws a three digit number.
618 //
619 //---------------------------------------------------------------------------
620 
DrINumber(signed int val,int x,int y,int imgBase) const621 void DBaseStatusBar::DrINumber (signed int val, int x, int y, int imgBase) const
622 {
623 	int oldval;
624 
625 	if (val > 999)
626 		val = 999;
627 	oldval = val;
628 	if (val < 0)
629 	{
630 		if (val < -9)
631 		{
632 			DrawImage (Images[imgLAME], x+1, y+1);
633 			return;
634 		}
635 		val = -val;
636 		DrawImage (Images[imgBase+val], x+18, y);
637 		DrawImage (Images[imgNEGATIVE], x+9, y);
638 		return;
639 	}
640 	if (val > 99)
641 	{
642 		DrawImage (Images[imgBase+val/100], x, y);
643 	}
644 	val = val % 100;
645 	if (val > 9 || oldval > 99)
646 	{
647 		DrawImage (Images[imgBase+val/10], x+9, y);
648 	}
649 	val = val % 10;
650 	DrawImage (Images[imgBase+val], x+18, y);
651 }
652 
653 //---------------------------------------------------------------------------
654 //
655 // PROC DrBNumber
656 //
657 // Draws an x digit number using the big font.
658 //
659 //---------------------------------------------------------------------------
660 
DrBNumber(signed int val,int x,int y,int size) const661 void DBaseStatusBar::DrBNumber (signed int val, int x, int y, int size) const
662 {
663 	bool neg;
664 	int i, w;
665 	int power;
666 	FTexture *pic;
667 
668 	pic = Images[imgBNumbers];
669 	w = (pic != NULL) ? pic->GetWidth() : 0;
670 
671 	if (val == 0)
672 	{
673 		if (pic != NULL)
674 		{
675 			DrawImage (pic, x - w, y);
676 		}
677 		return;
678 	}
679 
680 	if ( (neg = val < 0) )
681 	{
682 		val = -val;
683 		size--;
684 	}
685 	for (i = size-1, power = 10; i > 0; i--)
686 	{
687 		power *= 10;
688 	}
689 	if (val >= power)
690 	{
691 		val = power - 1;
692 	}
693 	while (val != 0 && size--)
694 	{
695 		x -= w;
696 		pic = Images[imgBNumbers + val % 10];
697 		val /= 10;
698 		if (pic != NULL)
699 		{
700 			DrawImage (pic, x, y);
701 		}
702 	}
703 	if (neg)
704 	{
705 		pic = Images[imgBNEGATIVE];
706 		if (pic != NULL)
707 		{
708 			DrawImage (pic, x - w, y);
709 		}
710 	}
711 }
712 
713 //---------------------------------------------------------------------------
714 //
715 // PROC DrSmallNumber
716 //
717 // Draws a small three digit number.
718 //
719 //---------------------------------------------------------------------------
720 
DrSmallNumber(int val,int x,int y) const721 void DBaseStatusBar::DrSmallNumber (int val, int x, int y) const
722 {
723 	int digit = 0;
724 
725 	if (val > 999)
726 	{
727 		val = 999;
728 	}
729 	if (val > 99)
730 	{
731 		digit = val / 100;
732 		DrawImage (Images[imgSmNumbers + digit], x, y);
733 		val -= digit * 100;
734 	}
735 	if (val > 9 || digit)
736 	{
737 		digit = val / 10;
738 		DrawImage (Images[imgSmNumbers + digit], x+4, y);
739 		val -= digit * 10;
740 	}
741 	DrawImage (Images[imgSmNumbers + val], x+8, y);
742 }
743 
744 //---------------------------------------------------------------------------
745 //
746 // PROC DrINumberOuter
747 //
748 // Draws a number outside the status bar, possibly scaled.
749 //
750 //---------------------------------------------------------------------------
751 
DrINumberOuter(signed int val,int x,int y,bool center,int w) const752 void DBaseStatusBar::DrINumberOuter (signed int val, int x, int y, bool center, int w) const
753 {
754 	bool negative = false;
755 
756 	x += w*2;
757 	if (val < 0)
758 	{
759 		negative = true;
760 		val = -val;
761 	}
762 	else if (val == 0)
763 	{
764 		screen->DrawTexture (Images[imgINumbers], x + 1, y + 1,
765 			DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
766 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
767 		screen->DrawTexture (Images[imgINumbers], x, y,
768 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
769 		return;
770 	}
771 
772 	int oval = val;
773 	int ox = x;
774 
775 	// First the shadow
776 	while (val != 0)
777 	{
778 		screen->DrawTexture (Images[imgINumbers + val % 10], x + 1, y + 1,
779 			DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
780 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
781 		x -= w;
782 		val /= 10;
783 	}
784 	if (negative)
785 	{
786 		screen->DrawTexture (Images[imgNEGATIVE], x + 1, y + 1,
787 			DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
788 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
789 	}
790 
791 	// Then the real deal
792 	val = oval;
793 	x = ox;
794 	while (val != 0)
795 	{
796 		screen->DrawTexture (Images[imgINumbers + val % 10], x, y,
797 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
798 		x -= w;
799 		val /= 10;
800 	}
801 	if (negative)
802 	{
803 		screen->DrawTexture (Images[imgNEGATIVE], x, y,
804 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
805 	}
806 }
807 
808 //---------------------------------------------------------------------------
809 //
810 // PROC DrBNumberOuter
811 //
812 // Draws a three digit number using the big font outside the status bar.
813 //
814 //---------------------------------------------------------------------------
815 
DrBNumberOuter(signed int val,int x,int y,int size) const816 void DBaseStatusBar::DrBNumberOuter (signed int val, int x, int y, int size) const
817 {
818 	int xpos;
819 	int w;
820 	bool negative = false;
821 	FTexture *pic;
822 
823 	pic = Images[imgBNumbers+3];
824 	if (pic != NULL)
825 	{
826 		w = pic->GetWidth();
827 	}
828 	else
829 	{
830 		w = 0;
831 	}
832 
833 	xpos = x + w/2 + (size-1)*w;
834 
835 	if (val == 0)
836 	{
837 		pic = Images[imgBNumbers];
838 		if (pic != NULL)
839 		{
840 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2 + 2, y + 2,
841 				DTA_HUDRules, HUD_Normal,
842 				DTA_Alpha, HR_SHADOW,
843 				DTA_FillColor, 0,
844 				TAG_DONE);
845 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2, y,
846 				DTA_HUDRules, HUD_Normal,
847 				TAG_DONE);
848 		}
849 		return;
850 	}
851 	else if (val < 0)
852 	{
853 		negative = true;
854 		val = -val;
855 	}
856 
857 	int oval = val;
858 	int oxpos = xpos;
859 
860 	// Draw shadow first
861 	while (val != 0)
862 	{
863 		pic = Images[val % 10 + imgBNumbers];
864 		if (pic != NULL)
865 		{
866 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2 + 2, y + 2,
867 				DTA_HUDRules, HUD_Normal,
868 				DTA_Alpha, HR_SHADOW,
869 				DTA_FillColor, 0,
870 				TAG_DONE);
871 		}
872 		val /= 10;
873 		xpos -= w;
874 	}
875 	if (negative)
876 	{
877 		pic = Images[imgBNEGATIVE];
878 		if (pic != NULL)
879 		{
880 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2 + 2, y + 2,
881 				DTA_HUDRules, HUD_Normal,
882 				DTA_Alpha, HR_SHADOW,
883 				DTA_FillColor, 0,
884 				TAG_DONE);
885 		}
886 	}
887 
888 	// Then draw the real thing
889 	val = oval;
890 	xpos = oxpos;
891 	while (val != 0)
892 	{
893 		pic = Images[val % 10 + imgBNumbers];
894 		if (pic != NULL)
895 		{
896 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2, y,
897 				DTA_HUDRules, HUD_Normal,
898 				TAG_DONE);
899 		}
900 		val /= 10;
901 		xpos -= w;
902 	}
903 	if (negative)
904 	{
905 		pic = Images[imgBNEGATIVE];
906 		if (pic != NULL)
907 		{
908 			screen->DrawTexture (pic, xpos - pic->GetWidth()/2, y,
909 				DTA_HUDRules, HUD_Normal,
910 				TAG_DONE);
911 		}
912 	}
913 }
914 
915 //---------------------------------------------------------------------------
916 //
917 // PROC DrBNumberOuter
918 //
919 // Draws a three digit number using the real big font outside the status bar.
920 //
921 //---------------------------------------------------------------------------
922 
DrBNumberOuterFont(signed int val,int x,int y,int size) const923 void DBaseStatusBar::DrBNumberOuterFont (signed int val, int x, int y, int size) const
924 {
925 	int xpos;
926 	int w, v;
927 	bool negative = false;
928 	FTexture *pic;
929 
930 	w = 0;
931 	BigFont->GetChar ('0', &w);
932 
933 	if (w > 1)
934 	{
935 		w--;
936 	}
937 	xpos = x + w/2 + (size-1)*w;
938 
939 	if (val == 0)
940 	{
941 		pic = BigFont->GetChar ('0', &v);
942 		screen->DrawTexture (pic, xpos - v/2 + 2, y + 2,
943 			DTA_HUDRules, HUD_Normal,
944 			DTA_Alpha, HR_SHADOW,
945 			DTA_FillColor, 0,
946 			DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
947 			TAG_DONE);
948 		screen->DrawTexture (pic, xpos - v/2, y,
949 			DTA_HUDRules, HUD_Normal,
950 			DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
951 			TAG_DONE);
952 		return;
953 	}
954 	else if (val < 0)
955 	{
956 		negative = true;
957 		val = -val;
958 	}
959 
960 	int oval = val;
961 	int oxpos = xpos;
962 
963 	// First the shadow
964 	while (val != 0)
965 	{
966 		pic = BigFont->GetChar ('0' + val % 10, &v);
967 		screen->DrawTexture (pic, xpos - v/2 + 2, y + 2,
968 			DTA_HUDRules, HUD_Normal,
969 			DTA_Alpha, HR_SHADOW,
970 			DTA_FillColor, 0,
971 			DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
972 			TAG_DONE);
973 		val /= 10;
974 		xpos -= w;
975 	}
976 	if (negative)
977 	{
978 		pic = BigFont->GetChar ('-', &v);
979 		if (pic != NULL)
980 		{
981 			screen->DrawTexture (pic, xpos - v/2 + 2, y + 2,
982 				DTA_HUDRules, HUD_Normal,
983 				DTA_Alpha, HR_SHADOW,
984 				DTA_FillColor, 0,
985 				DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
986 				TAG_DONE);
987 		}
988 	}
989 
990 	// Then the foreground number
991 	val = oval;
992 	xpos = oxpos;
993 	while (val != 0)
994 	{
995 		pic = BigFont->GetChar ('0' + val % 10, &v);
996 		screen->DrawTexture (pic, xpos - v/2, y,
997 			DTA_HUDRules, HUD_Normal,
998 			DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
999 			TAG_DONE);
1000 		val /= 10;
1001 		xpos -= w;
1002 	}
1003 	if (negative)
1004 	{
1005 		pic = BigFont->GetChar ('-', &v);
1006 		if (pic != NULL)
1007 		{
1008 			screen->DrawTexture (pic, xpos - v/2, y,
1009 				DTA_HUDRules, HUD_Normal,
1010 				DTA_Translation, BigFont->GetColorTranslation (CR_UNTRANSLATED),
1011 				TAG_DONE);
1012 		}
1013 	}
1014 }
1015 
1016 //---------------------------------------------------------------------------
1017 //
1018 // PROC DrSmallNumberOuter
1019 //
1020 // Draws a small three digit number outside the status bar.
1021 //
1022 //---------------------------------------------------------------------------
1023 
DrSmallNumberOuter(int val,int x,int y,bool center) const1024 void DBaseStatusBar::DrSmallNumberOuter (int val, int x, int y, bool center) const
1025 {
1026 	int digit = 0;
1027 
1028 	if (val > 999)
1029 	{
1030 		val = 999;
1031 	}
1032 	if (val > 99)
1033 	{
1034 		digit = val / 100;
1035 		screen->DrawTexture (Images[imgSmNumbers + digit], x, y,
1036 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
1037 		val -= digit * 100;
1038 	}
1039 	if (val > 9 || digit)
1040 	{
1041 		digit = val / 10;
1042 		screen->DrawTexture (Images[imgSmNumbers + digit], x+4, y,
1043 			DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
1044 		val -= digit * 10;
1045 	}
1046 	screen->DrawTexture (Images[imgSmNumbers + val], x+8, y,
1047 		DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal, TAG_DONE);
1048 }
1049 
1050 //---------------------------------------------------------------------------
1051 //
1052 // RefreshBackground
1053 //
1054 //---------------------------------------------------------------------------
1055 
RefreshBackground() const1056 void DBaseStatusBar::RefreshBackground () const
1057 {
1058 	int x, x2, y, ratio;
1059 
1060 	ratio = CheckRatio (SCREENWIDTH, SCREENHEIGHT);
1061 	x = (!(ratio & 3) || !Scaled) ? ST_X : SCREENWIDTH*(48-BaseRatioSizes[ratio][3])/(48*2);
1062 	y = x == ST_X && x > 0 ? ST_Y : ::ST_Y;
1063 
1064 	if(!CompleteBorder)
1065 	{
1066 		if(y < SCREENHEIGHT)
1067 		{
1068 			V_DrawBorder (x+1, y, SCREENWIDTH, y+1);
1069 			V_DrawBorder (x+1, SCREENHEIGHT-1, SCREENWIDTH, SCREENHEIGHT);
1070 		}
1071 	}
1072 	else
1073 	{
1074 		x = SCREENWIDTH;
1075 	}
1076 
1077 	if (x > 0)
1078 	{
1079 		if(!CompleteBorder)
1080 		{
1081 			x2 = !(ratio & 3) || !Scaled ? ST_X+HorizontalResolution :
1082 				SCREENWIDTH - (SCREENWIDTH*(48-BaseRatioSizes[ratio][3])+48*2-1)/(48*2);
1083 		}
1084 		else
1085 		{
1086 			x2 = SCREENWIDTH;
1087 		}
1088 
1089 		V_DrawBorder (0, y, x+1, SCREENHEIGHT);
1090 		V_DrawBorder (x2-1, y, SCREENWIDTH, SCREENHEIGHT);
1091 
1092 		if (setblocks >= 10)
1093 		{
1094 			FTexture *p = TexMan[gameinfo.Border.b];
1095 			if (p != NULL)
1096 			{
1097 				screen->FlatFill(0, y, x, y + p->GetHeight(), p, true);
1098 				screen->FlatFill(x2, y, SCREENWIDTH, y + p->GetHeight(), p, true);
1099 			}
1100 		}
1101 	}
1102 }
1103 
1104 //---------------------------------------------------------------------------
1105 //
1106 // DrawCrosshair
1107 //
1108 //---------------------------------------------------------------------------
1109 
DrawCrosshair()1110 void DBaseStatusBar::DrawCrosshair ()
1111 {
1112 	static DWORD prevcolor = 0xffffffff;
1113 	static int palettecolor = 0;
1114 
1115 	DWORD color;
1116 	fixed_t size;
1117 	int w, h;
1118 
1119 	// Don't draw the crosshair in chasecam mode
1120 	if (players[consoleplayer].cheats & CF_CHASECAM)
1121 		return;
1122 
1123 	ST_LoadCrosshair();
1124 
1125 	// Don't draw the crosshair if there is none
1126 	if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || camera->health <= 0)
1127 	{
1128 		return;
1129 	}
1130 
1131 	if (crosshairscale)
1132 	{
1133 		size = SCREENHEIGHT * FRACUNIT / 200;
1134 	}
1135 	else
1136 	{
1137 		size = FRACUNIT;
1138 	}
1139 
1140 	if (crosshairgrow)
1141 	{
1142 		size = FixedMul (size, CrosshairSize);
1143 	}
1144 	w = (CrosshairImage->GetWidth() * size) >> FRACBITS;
1145 	h = (CrosshairImage->GetHeight() * size) >> FRACBITS;
1146 
1147 	if (crosshairhealth)
1148 	{
1149 		int health = Scale(CPlayer->health, 100, CPlayer->mo->GetDefault()->health);
1150 
1151 		if (health >= 85)
1152 		{
1153 			color = 0x00ff00;
1154 		}
1155 		else
1156 		{
1157 			int red, green;
1158 			health -= 25;
1159 			if (health < 0)
1160 			{
1161 				health = 0;
1162 			}
1163 			if (health < 30)
1164 			{
1165 				red = 255;
1166 				green = health * 255 / 30;
1167 			}
1168 			else
1169 			{
1170 				red = (60 - health) * 255 / 30;
1171 				green = 255;
1172 			}
1173 			color = (red<<16) | (green<<8);
1174 		}
1175 	}
1176 	else
1177 	{
1178 		color = crosshaircolor;
1179 	}
1180 
1181 	if (color != prevcolor)
1182 	{
1183 		prevcolor = color;
1184 		palettecolor = ColorMatcher.Pick (RPART(color), GPART(color), BPART(color));
1185 	}
1186 
1187 	screen->DrawTexture (CrosshairImage,
1188 		viewwidth / 2 + viewwindowx,
1189 		viewheight / 2 + viewwindowy,
1190 		DTA_DestWidth, w,
1191 		DTA_DestHeight, h,
1192 		DTA_AlphaChannel, true,
1193 		DTA_FillColor, (palettecolor << 24) | (color & 0xFFFFFF),
1194 		TAG_DONE);
1195 }
1196 
1197 //---------------------------------------------------------------------------
1198 //
1199 // FlashCrosshair
1200 //
1201 //---------------------------------------------------------------------------
1202 
FlashCrosshair()1203 void DBaseStatusBar::FlashCrosshair ()
1204 {
1205 	CrosshairSize = XHAIRPICKUPSIZE;
1206 }
1207 
1208 //---------------------------------------------------------------------------
1209 //
1210 // DrawMessages
1211 //
1212 //---------------------------------------------------------------------------
1213 
DrawMessages(int layer,int bottom)1214 void DBaseStatusBar::DrawMessages (int layer, int bottom)
1215 {
1216 	DHUDMessage *msg = Messages[layer];
1217 	int visibility = 0;
1218 
1219 	if (viewactive)
1220 	{
1221 		visibility |= HUDMSG_NotWith3DView;
1222 	}
1223 	if (automapactive)
1224 	{
1225 		visibility |= viewactive ? HUDMSG_NotWithOverlayMap : HUDMSG_NotWithFullMap;
1226 	}
1227 	while (msg)
1228 	{
1229 		DHUDMessage *next = msg->Next;
1230 		msg->Draw (bottom, visibility);
1231 		msg = next;
1232 	}
1233 }
1234 
1235 //---------------------------------------------------------------------------
1236 //
1237 // Draw
1238 //
1239 //---------------------------------------------------------------------------
1240 
Draw(EHudState state)1241 void DBaseStatusBar::Draw (EHudState state)
1242 {
1243 	// HUD_AltHud state is for popups only
1244 	if (state == HUD_AltHud)
1245 		return;
1246 
1247 	char line[64+10];
1248 
1249 	if ((SB_state != 0 || BorderNeedRefresh) && state == HUD_StatusBar)
1250 	{
1251 		RefreshBackground ();
1252 	}
1253 
1254 	if (idmypos)
1255 	{ // Draw current coordinates
1256 		int height = SmallFont->GetHeight();
1257 		char labels[3] = { 'X', 'Y', 'Z' };
1258 		fixed_t *value;
1259 		int i;
1260 
1261 		int vwidth;
1262 		int vheight;
1263 		int xpos;
1264 		int y;
1265 
1266 		if (con_scaletext == 0)
1267 		{
1268 			vwidth = SCREENWIDTH;
1269 			vheight = SCREENHEIGHT;
1270 			xpos = vwidth - 80;
1271 			y = ::ST_Y - height;
1272 		}
1273 		else
1274 		{
1275 			vwidth = SCREENWIDTH/2;
1276 			vheight = SCREENHEIGHT/2;
1277 			xpos = vwidth - SmallFont->StringWidth("X: -00000")-6;
1278 			y = ::ST_Y/2 - height;
1279 		}
1280 
1281 		if (gameinfo.gametype == GAME_Strife)
1282 		{
1283 			if (con_scaletext == 0)
1284 				y -= height * 4;
1285 			else
1286 				y -= height * 2;
1287 		}
1288 
1289 		fixedvec3 pos = CPlayer->mo->Pos();
1290 		for (i = 2, value = &pos.z; i >= 0; y -= height, --value, --i)
1291 		{
1292 			mysnprintf (line, countof(line), "%c: %d", labels[i], *value >> FRACBITS);
1293 			screen->DrawText (SmallFont, CR_GREEN, xpos, y, line,
1294 				DTA_KeepRatio, true,
1295 				DTA_VirtualWidth, vwidth, DTA_VirtualHeight, vheight,
1296 				TAG_DONE);
1297 			V_SetBorderNeedRefresh();
1298 		}
1299 	}
1300 
1301 	if (viewactive)
1302 	{
1303 		if (CPlayer && CPlayer->camera && CPlayer->camera->player)
1304 		{
1305 			DrawCrosshair ();
1306 		}
1307 	}
1308 	else if (automapactive)
1309 	{
1310 		int y, time = Tics2Seconds(level.time), height;
1311 		int totaltime = Tics2Seconds(level.totaltime);
1312 		EColorRange highlight = (gameinfo.gametype & GAME_DoomChex) ?
1313 			CR_UNTRANSLATED : CR_YELLOW;
1314 
1315 		height = SmallFont->GetHeight () * CleanYfac;
1316 
1317 		// Draw timer
1318 		y = 8;
1319 		if (am_showtime)
1320 		{
1321 			mysnprintf (line, countof(line), "%02d:%02d:%02d", time/3600, (time%3600)/60, time%60);	// Time
1322 			screen->DrawText (SmallFont, CR_GREY, SCREENWIDTH - 80*CleanXfac, y, line, DTA_CleanNoMove, true, TAG_DONE);
1323 			y+=8*CleanYfac;
1324 		}
1325 		if (am_showtotaltime)
1326 		{
1327 			mysnprintf (line, countof(line), "%02d:%02d:%02d", totaltime/3600, (totaltime%3600)/60, totaltime%60);	// Total time
1328 			screen->DrawText (SmallFont, CR_GREY, SCREENWIDTH - 80*CleanXfac, y, line, DTA_CleanNoMove, true, TAG_DONE);
1329 		}
1330 
1331 		// Draw map name
1332 		y = ::ST_Y - height;
1333 		if (gameinfo.gametype == GAME_Heretic && SCREENWIDTH > 320 && !Scaled)
1334 		{
1335 			y -= 8;
1336 		}
1337 		else if (gameinfo.gametype == GAME_Hexen)
1338 		{
1339 			if (Scaled)
1340 			{
1341 				y -= Scale (11, SCREENHEIGHT, 200);
1342 			}
1343 			else
1344 			{
1345 				if (SCREENWIDTH < 640)
1346 				{
1347 					y -= 12;
1348 				}
1349 				else
1350 				{ // Get past the tops of the gargoyles' wings
1351 					y -= 28;
1352 				}
1353 			}
1354 		}
1355 		else if (gameinfo.gametype == GAME_Strife)
1356 		{
1357 			if (Scaled)
1358 			{
1359 				y -= Scale (8, SCREENHEIGHT, 200);
1360 			}
1361 			else
1362 			{
1363 				y -= 8;
1364 			}
1365 		}
1366 		FString mapname;
1367 
1368 		ST_FormatMapName(mapname, TEXTCOLOR_GREY);
1369 		screen->DrawText (SmallFont, highlight,
1370 			(SCREENWIDTH - SmallFont->StringWidth (mapname)*CleanXfac)/2, y, mapname,
1371 			DTA_CleanNoMove, true, TAG_DONE);
1372 
1373 		if (!deathmatch)
1374 		{
1375 			int y = 8;
1376 
1377 			// Draw monster count
1378 			if (am_showmonsters)
1379 			{
1380 				mysnprintf (line, countof(line), "%s" TEXTCOLOR_GREY " %d/%d",
1381 					GStrings("AM_MONSTERS"), level.killed_monsters, level.total_monsters);
1382 				screen->DrawText (SmallFont, highlight, 8, y, line,
1383 					DTA_CleanNoMove, true, TAG_DONE);
1384 				y += height;
1385 			}
1386 
1387 			// Draw secret count
1388 			if (am_showsecrets)
1389 			{
1390 				mysnprintf (line, countof(line), "%s" TEXTCOLOR_GREY " %d/%d",
1391 					GStrings("AM_SECRETS"), level.found_secrets, level.total_secrets);
1392 				screen->DrawText (SmallFont, highlight, 8, y, line,
1393 					DTA_CleanNoMove, true, TAG_DONE);
1394 				y += height;
1395 			}
1396 
1397 			// Draw item count
1398 			if (am_showitems)
1399 			{
1400 				mysnprintf (line, countof(line), "%s" TEXTCOLOR_GREY " %d/%d",
1401 					GStrings("AM_ITEMS"), level.found_items, level.total_items);
1402 				screen->DrawText (SmallFont, highlight, 8, y, line,
1403 					DTA_CleanNoMove, true, TAG_DONE);
1404 			}
1405 		}
1406 	}
1407 }
1408 
1409 
DrawLog()1410 void DBaseStatusBar::DrawLog ()
1411 {
1412 	int hudwidth, hudheight;
1413 
1414 	if (CPlayer->LogText && *CPlayer->LogText)
1415 	{
1416 		// This uses the same scaling as regular HUD messages
1417 		switch (con_scaletext)
1418 		{
1419 		default:
1420 			hudwidth = SCREENWIDTH;
1421 			hudheight = SCREENHEIGHT;
1422 			break;
1423 
1424 		case 1:
1425 			hudwidth = SCREENWIDTH / CleanXfac;
1426 			hudheight = SCREENHEIGHT / CleanYfac;
1427 			break;
1428 
1429 		case 2:
1430 			hudwidth = SCREENWIDTH / 2;
1431 			hudheight = SCREENHEIGHT / 2;
1432 			break;
1433 		}
1434 
1435 		int linelen = hudwidth<640? Scale(hudwidth,9,10)-40 : 560;
1436 		FBrokenLines *lines = V_BreakLines (SmallFont, linelen, CPlayer->LogText);
1437 		int height = 20;
1438 
1439 		for (int i = 0; lines[i].Width != -1; i++) height += SmallFont->GetHeight () + 1;
1440 
1441 		int x,y,w;
1442 
1443 		if (linelen<560)
1444 		{
1445 			x=hudwidth/20;
1446 			y=hudheight/8;
1447 			w=hudwidth-2*x;
1448 		}
1449 		else
1450 		{
1451 			x=(hudwidth>>1)-300;
1452 			y=hudheight*3/10-(height>>1);
1453 			if (y<0) y=0;
1454 			w=600;
1455 		}
1456 		screen->Dim(0, 0.5f, Scale(x, SCREENWIDTH, hudwidth), Scale(y, SCREENHEIGHT, hudheight),
1457 							 Scale(w, SCREENWIDTH, hudwidth), Scale(height, SCREENHEIGHT, hudheight));
1458 		x+=20;
1459 		y+=10;
1460 		for (int i = 0; lines[i].Width != -1; i++)
1461 		{
1462 
1463 			screen->DrawText (SmallFont, CR_UNTRANSLATED, x, y, lines[i].Text,
1464 				DTA_KeepRatio, true,
1465 				DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, TAG_DONE);
1466 			y += SmallFont->GetHeight ()+1;
1467 		}
1468 
1469 		V_FreeBrokenLines (lines);
1470 	}
1471 }
1472 
MustDrawLog(EHudState)1473 bool DBaseStatusBar::MustDrawLog(EHudState)
1474 {
1475 	return true;
1476 }
1477 
SetMugShotState(const char * stateName,bool waitTillDone,bool reset)1478 void DBaseStatusBar::SetMugShotState(const char *stateName, bool waitTillDone, bool reset)
1479 {
1480 }
1481 
1482 //---------------------------------------------------------------------------
1483 //
1484 // DrawBottomStuff
1485 //
1486 //---------------------------------------------------------------------------
1487 
DrawBottomStuff(EHudState state)1488 void DBaseStatusBar::DrawBottomStuff (EHudState state)
1489 {
1490 	DrawMessages (HUDMSGLayer_UnderHUD, (state == HUD_StatusBar) ? ::ST_Y : SCREENHEIGHT);
1491 }
1492 
1493 //---------------------------------------------------------------------------
1494 //
1495 // DrawTopStuff
1496 //
1497 //---------------------------------------------------------------------------
1498 
DrawTopStuff(EHudState state)1499 void DBaseStatusBar::DrawTopStuff (EHudState state)
1500 {
1501 	if (demoplayback && demover != DEMOGAMEVERSION)
1502 	{
1503 		screen->DrawText (SmallFont, CR_TAN, 0, ST_Y - 40 * CleanYfac,
1504 			"Demo was recorded with a different version\n"
1505 			"of " GAMENAME ". Expect it to go out of sync.",
1506 			DTA_CleanNoMove, true, TAG_DONE);
1507 	}
1508 
1509 	DrawPowerups ();
1510 	if (automapactive && !viewactive)
1511 	{
1512 		DrawMessages (HUDMSGLayer_OverMap, (state == HUD_StatusBar) ? ::ST_Y : SCREENHEIGHT);
1513 	}
1514 	DrawMessages (HUDMSGLayer_OverHUD, (state == HUD_StatusBar) ? ::ST_Y : SCREENHEIGHT);
1515 	DrawConsistancy ();
1516 	DrawWaiting ();
1517 	if (ShowLog && MustDrawLog(state)) DrawLog ();
1518 
1519 	if (noisedebug)
1520 	{
1521 		S_NoiseDebug ();
1522 	}
1523 }
1524 
1525 //---------------------------------------------------------------------------
1526 //
1527 // DrawPowerups
1528 //
1529 //---------------------------------------------------------------------------
1530 
DrawPowerups()1531 void DBaseStatusBar::DrawPowerups ()
1532 {
1533 	// Each icon gets a 32x32 block to draw itself in.
1534 	int x, y;
1535 	AInventory *item;
1536 	const int yshift = SmallFont->GetHeight();
1537 
1538 	x = -20;
1539 	y = 17
1540 		+ (ST_IsTimeVisible()    ? yshift : 0)
1541 		+ (ST_IsLatencyVisible() ? yshift : 0);
1542 	for (item = CPlayer->mo->Inventory; item != NULL; item = item->Inventory)
1543 	{
1544 		if (item->DrawPowerup (x, y))
1545 		{
1546 			x -= POWERUPICONSIZE;
1547 			if (x < -POWERUPICONSIZE*5)
1548 			{
1549 				x = -20;
1550 				y += POWERUPICONSIZE*2;
1551 			}
1552 		}
1553 	}
1554 }
1555 
1556 //---------------------------------------------------------------------------
1557 //
1558 // BlendView
1559 //
1560 //---------------------------------------------------------------------------
1561 
BlendView(float blend[4])1562 void DBaseStatusBar::BlendView (float blend[4])
1563 {
1564 	V_AddBlend (BaseBlendR / 255.f, BaseBlendG / 255.f, BaseBlendB / 255.f, BaseBlendA, blend);
1565 	V_AddPlayerBlend(CPlayer, blend, 1.0f, 228);
1566 
1567 	if (screen->Accel2D || (CPlayer->camera != NULL && menuactive == MENU_Off && ConsoleState == c_up))
1568 	{
1569 		player_t *player = (CPlayer->camera != NULL && CPlayer->camera->player != NULL) ? CPlayer->camera->player : CPlayer;
1570 		V_AddBlend (player->BlendR, player->BlendG, player->BlendB, player->BlendA, blend);
1571 	}
1572 
1573 	V_SetBlend ((int)(blend[0] * 255.0f), (int)(blend[1] * 255.0f),
1574 				(int)(blend[2] * 255.0f), (int)(blend[3] * 256.0f));
1575 }
1576 
DrawConsistancy() const1577 void DBaseStatusBar::DrawConsistancy () const
1578 {
1579 	static bool firsttime = true;
1580 	int i;
1581 	char conbuff[64], *buff_p;
1582 
1583 	if (!netgame)
1584 		return;
1585 
1586 	buff_p = NULL;
1587 	for (i = 0; i < MAXPLAYERS; i++)
1588 	{
1589 		if (playeringame[i] && players[i].inconsistant)
1590 		{
1591 			if (buff_p == NULL)
1592 			{
1593 				strcpy (conbuff, "Out of sync with:");
1594 				buff_p = conbuff + 17;
1595 			}
1596 			*buff_p++ = ' ';
1597 			*buff_p++ = '1' + i;
1598 			*buff_p = 0;
1599 		}
1600 	}
1601 
1602 	if (buff_p != NULL)
1603 	{
1604 		if (firsttime)
1605 		{
1606 			firsttime = false;
1607 			if (debugfile)
1608 			{
1609 				fprintf (debugfile, "%s as of tic %d (%d)\n", conbuff,
1610 					players[1-consoleplayer].inconsistant,
1611 					players[1-consoleplayer].inconsistant/ticdup);
1612 			}
1613 		}
1614 		screen->DrawText (SmallFont, CR_GREEN,
1615 			(screen->GetWidth() - SmallFont->StringWidth (conbuff)*CleanXfac) / 2,
1616 			0, conbuff, DTA_CleanNoMove, true, TAG_DONE);
1617 		BorderTopRefresh = screen->GetPageCount ();
1618 	}
1619 }
1620 
DrawWaiting() const1621 void DBaseStatusBar::DrawWaiting () const
1622 {
1623 	int i;
1624 	char conbuff[64], *buff_p;
1625 
1626 	if (!netgame)
1627 		return;
1628 
1629 	buff_p = NULL;
1630 	for (i = 0; i < MAXPLAYERS; i++)
1631 	{
1632 		if (playeringame[i] && players[i].waiting)
1633 		{
1634 			if (buff_p == NULL)
1635 			{
1636 				strcpy (conbuff, "Waiting for:");
1637 				buff_p = conbuff + 12;
1638 			}
1639 			*buff_p++ = ' ';
1640 			*buff_p++ = '1' + i;
1641 			*buff_p = 0;
1642 		}
1643 	}
1644 
1645 	if (buff_p != NULL)
1646 	{
1647 		screen->DrawText (SmallFont, CR_ORANGE,
1648 			(screen->GetWidth() - SmallFont->StringWidth (conbuff)*CleanXfac) / 2,
1649 			SmallFont->GetHeight()*CleanYfac, conbuff, DTA_CleanNoMove, true, TAG_DONE);
1650 		BorderTopRefresh = screen->GetPageCount ();
1651 	}
1652 }
1653 
FlashItem(const PClass * itemtype)1654 void DBaseStatusBar::FlashItem (const PClass *itemtype)
1655 {
1656 }
1657 
NewGame()1658 void DBaseStatusBar::NewGame ()
1659 {
1660 }
1661 
SetInteger(int pname,int param)1662 void DBaseStatusBar::SetInteger (int pname, int param)
1663 {
1664 }
1665 
ShowPop(int popnum)1666 void DBaseStatusBar::ShowPop (int popnum)
1667 {
1668 	ShowLog = (popnum == POP_Log && !ShowLog);
1669 }
1670 
ReceivedWeapon(AWeapon * weapon)1671 void DBaseStatusBar::ReceivedWeapon (AWeapon *weapon)
1672 {
1673 }
1674 
Serialize(FArchive & arc)1675 void DBaseStatusBar::Serialize (FArchive &arc)
1676 {
1677 	if (SaveVersion < 3821)
1678 	{
1679 		memset(Messages, 0, sizeof(Messages));
1680 		arc << Messages[HUDMSGLayer_Default];
1681 	}
1682 	else
1683 	{
1684 		for (unsigned int i = 0; i < countof(Messages); ++i)
1685 		{
1686 			arc << Messages[i];
1687 		}
1688 	}
1689 }
1690 
ScreenSizeChanged()1691 void DBaseStatusBar::ScreenSizeChanged ()
1692 {
1693 	st_scale.Callback ();
1694 	ST_SetNeedRefresh();
1695 
1696 	for (unsigned int i = 0; i < countof(Messages); ++i)
1697 	{
1698 		DHUDMessage *message = Messages[i];
1699 		while (message != NULL)
1700 		{
1701 			message->ScreenSizeChanged ();
1702 			message = message->Next;
1703 		}
1704 	}
1705 }
1706 
1707 //---------------------------------------------------------------------------
1708 //
1709 // ValidateInvFirst
1710 //
1711 // Returns an inventory item that, when drawn as the first item, is sure to
1712 // include the selected item in the inventory bar.
1713 //
1714 //---------------------------------------------------------------------------
1715 
ValidateInvFirst(int numVisible) const1716 AInventory *DBaseStatusBar::ValidateInvFirst (int numVisible) const
1717 {
1718 	AInventory *item;
1719 	int i;
1720 
1721 	if (CPlayer->mo->InvFirst == NULL)
1722 	{
1723 		CPlayer->mo->InvFirst = CPlayer->mo->FirstInv();
1724 		if (CPlayer->mo->InvFirst == NULL)
1725 		{ // Nothing to show
1726 			return NULL;
1727 		}
1728 	}
1729 
1730 	assert (CPlayer->mo->InvFirst->Owner == CPlayer->mo);
1731 
1732 	// If there are fewer than numVisible items shown, see if we can shift the
1733 	// view left to show more.
1734 	for (i = 0, item = CPlayer->mo->InvFirst; item != NULL && i < numVisible; ++i, item = item->NextInv())
1735 	{ }
1736 
1737 	while (i < numVisible)
1738 	{
1739 		item = CPlayer->mo->InvFirst->PrevInv ();
1740 		if (item == NULL)
1741 		{
1742 			break;
1743 		}
1744 		else
1745 		{
1746 			CPlayer->mo->InvFirst = item;
1747 			++i;
1748 		}
1749 	}
1750 
1751 	if (CPlayer->mo->InvSel == NULL)
1752 	{
1753 		// Nothing selected, so don't move the view.
1754 		return CPlayer->mo->InvFirst == NULL ? CPlayer->mo->Inventory : CPlayer->mo->InvFirst;
1755 	}
1756 	else
1757 	{
1758 		// Check if InvSel is already visible
1759 		for (item = CPlayer->mo->InvFirst, i = numVisible;
1760 			 item != NULL && i != 0;
1761 			 item = item->NextInv(), --i)
1762 		{
1763 			if (item == CPlayer->mo->InvSel)
1764 			{
1765 				return CPlayer->mo->InvFirst;
1766 			}
1767 		}
1768 		// Check if InvSel is to the right of the visible range
1769 		for (i = 1; item != NULL; item = item->NextInv(), ++i)
1770 		{
1771 			if (item == CPlayer->mo->InvSel)
1772 			{
1773 				// Found it. Now advance InvFirst
1774 				for (item = CPlayer->mo->InvFirst; i != 0; --i)
1775 				{
1776 					item = item->NextInv();
1777 				}
1778 				return item;
1779 			}
1780 		}
1781 		// Check if InvSel is to the left of the visible range
1782 		for (item = CPlayer->mo->Inventory;
1783 			item != CPlayer->mo->InvSel;
1784 			item = item->NextInv())
1785 		{ }
1786 		if (item != NULL)
1787 		{
1788 			// Found it, so let it become the first item shown
1789 			return item;
1790 		}
1791 		// Didn't find the selected item, so don't move the view.
1792 		// This should never happen, so let debug builds assert.
1793 		assert (item != NULL);
1794 		return CPlayer->mo->InvFirst;
1795 	}
1796 }
1797 
1798 //============================================================================
1799 //
1800 // DBaseStatusBar :: GetCurrentAmmo
1801 //
1802 // Returns the types and amounts of ammo used by the current weapon. If the
1803 // weapon only uses one type of ammo, it is always returned as ammo1.
1804 //
1805 //============================================================================
1806 
GetCurrentAmmo(AAmmo * & ammo1,AAmmo * & ammo2,int & ammocount1,int & ammocount2) const1807 void DBaseStatusBar::GetCurrentAmmo (AAmmo *&ammo1, AAmmo *&ammo2, int &ammocount1, int &ammocount2) const
1808 {
1809 	if (CPlayer->ReadyWeapon != NULL)
1810 	{
1811 		ammo1 = CPlayer->ReadyWeapon->Ammo1;
1812 		ammo2 = CPlayer->ReadyWeapon->Ammo2;
1813 		if (ammo1 == NULL)
1814 		{
1815 			ammo1 = ammo2;
1816 			ammo2 = NULL;
1817 		}
1818 	}
1819 	else
1820 	{
1821 		ammo1 = ammo2 = NULL;
1822 	}
1823 	ammocount1 = ammo1 != NULL ? ammo1->Amount : 0;
1824 	ammocount2 = ammo2 != NULL ? ammo2->Amount : 0;
1825 }
1826 
1827 //============================================================================
1828 //
1829 // CCMD showpop
1830 //
1831 // Asks the status bar to show a pop screen.
1832 //
1833 //============================================================================
1834 
CCMD(showpop)1835 CCMD (showpop)
1836 {
1837 	if (argv.argc() != 2)
1838 	{
1839 		Printf ("Usage: showpop <popnumber>\n");
1840 	}
1841 	else if (StatusBar != NULL)
1842 	{
1843 		int popnum = atoi (argv[1]);
1844 		if (popnum < 0)
1845 		{
1846 			popnum = 0;
1847 		}
1848 		StatusBar->ShowPop (popnum);
1849 	}
1850 }
1851