1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  st_stuff.c
12 /// \brief Status bar code
13 ///        Does the face/direction indicator animatin.
14 ///        Does palette indicators as well (red pain/berserk, bright pickup)
15 
16 #include "doomdef.h"
17 #include "g_game.h"
18 #include "r_local.h"
19 #include "p_local.h"
20 #include "f_finale.h"
21 #include "st_stuff.h"
22 #include "i_video.h"
23 #include "v_video.h"
24 #include "z_zone.h"
25 #include "hu_stuff.h"
26 #include "console.h"
27 #include "s_sound.h"
28 #include "i_system.h"
29 #include "m_menu.h"
30 #include "m_cheat.h"
31 #include "m_misc.h" // moviemode
32 #include "m_anigif.h" // cv_gif_downscale
33 #include "p_setup.h" // NiGHTS grading
34 
35 //random index
36 #include "m_random.h"
37 
38 // item finder
39 #include "m_cond.h"
40 
41 #ifdef HWRENDER
42 #include "hardware/hw_main.h"
43 #endif
44 
45 #include "lua_hud.h"
46 
47 UINT16 objectsdrawn = 0;
48 
49 //
50 // STATUS BAR DATA
51 //
52 
53 patch_t *faceprefix[MAXSKINS]; // face status patches
54 patch_t *superprefix[MAXSKINS]; // super face status patches
55 
56 // ------------------------------------------
57 //             status bar overlay
58 // ------------------------------------------
59 
60 // icons for overlay
61 patch_t *sboscore; // Score logo
62 patch_t *sbotime; // Time logo
63 patch_t *sbocolon; // Colon for time
64 patch_t *sboperiod; // Period for time centiseconds
65 patch_t *livesback; // Lives icon background
66 patch_t *stlivex;
67 static patch_t *nrec_timer; // Timer for NiGHTS records
68 static patch_t *sborings;
69 static patch_t *slidgame;
70 static patch_t *slidtime;
71 static patch_t *slidover;
72 static patch_t *sboredrings;
73 static patch_t *sboredtime;
74 static patch_t *getall; // Special Stage HUD
75 static patch_t *timeup; // Special Stage HUD
76 static patch_t *hunthoming[6];
77 static patch_t *itemhoming[6];
78 static patch_t *race1;
79 static patch_t *race2;
80 static patch_t *race3;
81 static patch_t *racego;
82 static patch_t *nightslink;
83 static patch_t *curweapon;
84 static patch_t *normring;
85 static patch_t *bouncering;
86 static patch_t *infinityring;
87 static patch_t *autoring;
88 static patch_t *explosionring;
89 static patch_t *scatterring;
90 static patch_t *grenadering;
91 static patch_t *railring;
92 static patch_t *jumpshield;
93 static patch_t *forceshield;
94 static patch_t *ringshield;
95 static patch_t *watershield;
96 static patch_t *bombshield;
97 static patch_t *pityshield;
98 static patch_t *pinkshield;
99 static patch_t *flameshield;
100 static patch_t *bubbleshield;
101 static patch_t *thundershield;
102 static patch_t *invincibility;
103 static patch_t *sneakers;
104 static patch_t *gravboots;
105 static patch_t *nonicon;
106 static patch_t *nonicon2;
107 static patch_t *bluestat;
108 static patch_t *byelstat;
109 static patch_t *orngstat;
110 static patch_t *redstat;
111 static patch_t *yelstat;
112 static patch_t *nbracket;
113 static patch_t *nring;
114 static patch_t *nhud[12];
115 static patch_t *nsshud;
116 static patch_t *nbon[12];
117 static patch_t *nssbon;
118 static patch_t *narrow[9];
119 static patch_t *nredar[8]; // Red arrow
120 static patch_t *drillbar;
121 static patch_t *drillfill[3];
122 static patch_t *capsulebar;
123 static patch_t *capsulefill;
124 patch_t *ngradeletters[7];
125 static patch_t *minus5sec;
126 static patch_t *minicaps;
127 static patch_t *gotrflag;
128 static patch_t *gotbflag;
129 static patch_t *fnshico;
130 
131 static boolean facefreed[MAXPLAYERS];
132 
133 hudinfo_t hudinfo[NUMHUDITEMS] =
134 {
135 	{  16, 176, V_SNAPTOLEFT|V_SNAPTOBOTTOM}, // HUD_LIVES
136 
137 	{  16,  42, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_RINGS
138 	{  96,  42, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_RINGSNUM
139 	{ 120,  42, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_RINGSNUMTICS
140 
141 	{  16,  10, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_SCORE
142 	{ 120,  10, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_SCORENUM
143 
144 	{  16,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_TIME
145 	{  72,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_MINUTES
146 	{  72,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_TIMECOLON
147 	{  96,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_SECONDS
148 	{  96,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_TIMETICCOLON
149 	{ 120,  26, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_TICS
150 
151 	{   0,  56, V_SNAPTOLEFT|V_SNAPTOTOP}, // HUD_SS_TOTALRINGS
152 
153 	{ 110,  93, 0}, // HUD_GETRINGS
154 	{ 160,  93, 0}, // HUD_GETRINGSNUM
155 	{ 124, 160, 0}, // HUD_TIMELEFT
156 	{ 168, 176, 0}, // HUD_TIMELEFTNUM
157 	{ 130,  93, 0}, // HUD_TIMEUP
158 	{ 152, 168, 0}, // HUD_HUNTPICS
159 
160 	{ 288, 176, V_SNAPTORIGHT|V_SNAPTOBOTTOM}, // HUD_POWERUPS
161 };
162 
163 //
164 // STATUS BAR CODE
165 //
166 
ST_SameTeam(player_t * a,player_t * b)167 boolean ST_SameTeam(player_t *a, player_t *b)
168 {
169 	// Just pipe team messages to everyone in co-op or race.
170 	if (!G_RingSlingerGametype())
171 		return true;
172 
173 	// Spectator chat.
174 	if (a->spectator && b->spectator)
175 		return true;
176 
177 	// Team chat.
178 	if (G_GametypeHasTeams())
179 		return a->ctfteam == b->ctfteam;
180 
181 	if (G_TagGametype())
182 		return ((a->pflags & PF_TAGIT) == (b->pflags & PF_TAGIT));
183 
184 	return false;
185 }
186 
187 static boolean st_stopped = true;
188 
ST_Ticker(boolean run)189 void ST_Ticker(boolean run)
190 {
191 	if (st_stopped)
192 		return;
193 
194 	if (run)
195 		ST_runTitleCard();
196 }
197 
198 // 0 is default, any others are special palettes.
199 INT32 st_palette = 0;
200 INT32 st_translucency = 10;
201 
ST_doPaletteStuff(void)202 void ST_doPaletteStuff(void)
203 {
204 	INT32 palette;
205 
206 	if (stplyr && stplyr->flashcount)
207 		palette = stplyr->flashpal;
208 	else
209 		palette = 0;
210 
211 #ifdef HWRENDER
212 	if (rendermode == render_opengl)
213 		palette = 0; // No flashpals here in OpenGL
214 #endif
215 
216 	if (palette != st_palette)
217 	{
218 		st_palette = palette;
219 
220 		if (rendermode != render_none)
221 		{
222 			V_SetPaletteLump(GetPalette()); // Reset the palette
223 			if (!splitscreen)
224 				V_SetPalette(palette);
225 		}
226 	}
227 }
228 
ST_UnloadGraphics(void)229 void ST_UnloadGraphics(void)
230 {
231 	Patch_FreeTag(PU_HUDGFX);
232 }
233 
ST_LoadGraphics(void)234 void ST_LoadGraphics(void)
235 {
236 	int i;
237 
238 	// SRB2 border patch
239 	// st_borderpatchnum = W_GetNumForName("GFZFLR01");
240 	// scr_borderpatch = W_CacheLumpNum(st_borderpatchnum, PU_HUDGFX);
241 
242 	// the original Doom uses 'STF' as base name for all face graphics
243 	// Graue 04-08-2004: face/name graphics are now indexed by skins
244 	//                   but load them in R_AddSkins, that gets called
245 	//                   first anyway
246 	// cache the status bar overlay icons (fullscreen mode)
247 
248 	// Prefix "STT" is whitelisted (doesn't trigger ISGAMEMODIFIED), btw
249 	sborings = W_CachePatchName("STTRINGS", PU_HUDGFX);
250 	sboredrings = W_CachePatchName("STTRRING", PU_HUDGFX);
251 	sboscore = W_CachePatchName("STTSCORE", PU_HUDGFX);
252 	sbotime = W_CachePatchName("STTTIME", PU_HUDGFX); // Time logo
253 	sboredtime = W_CachePatchName("STTRTIME", PU_HUDGFX);
254 	sbocolon = W_CachePatchName("STTCOLON", PU_HUDGFX); // Colon for time
255 	sboperiod = W_CachePatchName("STTPERIO", PU_HUDGFX); // Period for time centiseconds
256 
257 	slidgame = W_CachePatchName("SLIDGAME", PU_HUDGFX);
258 	slidtime = W_CachePatchName("SLIDTIME", PU_HUDGFX);
259 	slidover = W_CachePatchName("SLIDOVER", PU_HUDGFX);
260 
261 	stlivex = W_CachePatchName("STLIVEX", PU_HUDGFX);
262 	livesback = W_CachePatchName("STLIVEBK", PU_HUDGFX);
263 	nrec_timer = W_CachePatchName("NGRTIMER", PU_HUDGFX); // Timer for NiGHTS
264 	getall = W_CachePatchName("GETALL", PU_HUDGFX); // Special Stage HUD
265 	timeup = W_CachePatchName("TIMEUP", PU_HUDGFX); // Special Stage HUD
266 	race1 = W_CachePatchName("RACE1", PU_HUDGFX);
267 	race2 = W_CachePatchName("RACE2", PU_HUDGFX);
268 	race3 = W_CachePatchName("RACE3", PU_HUDGFX);
269 	racego = W_CachePatchName("RACEGO", PU_HUDGFX);
270 	nightslink = W_CachePatchName("NGHTLINK", PU_HUDGFX);
271 
272 	for (i = 0; i < 6; ++i)
273 	{
274 		hunthoming[i] = W_CachePatchName(va("HOMING%d", i+1), PU_HUDGFX);
275 		itemhoming[i] = W_CachePatchName(va("HOMITM%d", i+1), PU_HUDGFX);
276 	}
277 
278 	curweapon = W_CachePatchName("CURWEAP", PU_HUDGFX);
279 	normring = W_CachePatchName("RINGIND", PU_HUDGFX);
280 	bouncering = W_CachePatchName("BNCEIND", PU_HUDGFX);
281 	infinityring = W_CachePatchName("INFNIND", PU_HUDGFX);
282 	autoring = W_CachePatchName("AUTOIND", PU_HUDGFX);
283 	explosionring = W_CachePatchName("BOMBIND", PU_HUDGFX);
284 	scatterring = W_CachePatchName("SCATIND", PU_HUDGFX);
285 	grenadering = W_CachePatchName("GRENIND", PU_HUDGFX);
286 	railring = W_CachePatchName("RAILIND", PU_HUDGFX);
287 	jumpshield = W_CachePatchName("TVWWICON", PU_HUDGFX);
288 	forceshield = W_CachePatchName("TVFOICON", PU_HUDGFX);
289 	ringshield = W_CachePatchName("TVATICON", PU_HUDGFX);
290 	watershield = W_CachePatchName("TVELICON", PU_HUDGFX);
291 	bombshield = W_CachePatchName("TVARICON", PU_HUDGFX);
292 	pityshield = W_CachePatchName("TVPIICON", PU_HUDGFX);
293 	pinkshield = W_CachePatchName("TVPPICON", PU_HUDGFX);
294 	flameshield = W_CachePatchName("TVFLICON", PU_HUDGFX);
295 	bubbleshield = W_CachePatchName("TVBBICON", PU_HUDGFX);
296 	thundershield = W_CachePatchName("TVZPICON", PU_HUDGFX);
297 	invincibility = W_CachePatchName("TVIVICON", PU_HUDGFX);
298 	sneakers = W_CachePatchName("TVSSICON", PU_HUDGFX);
299 	gravboots = W_CachePatchName("TVGVICON", PU_HUDGFX);
300 
301 	tagico = W_CachePatchName("TAGICO", PU_HUDGFX);
302 	gotrflag = W_CachePatchName("GOTRFLAG", PU_HUDGFX);
303 	gotbflag = W_CachePatchName("GOTBFLAG", PU_HUDGFX);
304 	fnshico = W_CachePatchName("FNSHICO", PU_HUDGFX);
305 	nonicon = W_CachePatchName("NONICON", PU_HUDGFX);
306 	nonicon2 = W_CachePatchName("NONICON2", PU_HUDGFX);
307 
308 	// NiGHTS HUD things
309 	bluestat = W_CachePatchName("BLUESTAT", PU_HUDGFX);
310 	byelstat = W_CachePatchName("BYELSTAT", PU_HUDGFX);
311 	orngstat = W_CachePatchName("ORNGSTAT", PU_HUDGFX);
312 	redstat = W_CachePatchName("REDSTAT", PU_HUDGFX);
313 	yelstat = W_CachePatchName("YELSTAT", PU_HUDGFX);
314 	nbracket = W_CachePatchName("NBRACKET", PU_HUDGFX);
315 	nring = W_CachePatchName("NRNG1", PU_HUDGFX);
316 	for (i = 0; i < 12; ++i)
317 	{
318 		nhud[i] = W_CachePatchName(va("NHUD%d", i+1), PU_HUDGFX);
319 		nbon[i] = W_CachePatchName(va("NBON%d", i+1), PU_HUDGFX);
320 	}
321 	nsshud = W_CachePatchName("NSSHUD", PU_HUDGFX);
322 	nssbon = W_CachePatchName("NSSBON", PU_HUDGFX);
323 	minicaps = W_CachePatchName("MINICAPS", PU_HUDGFX);
324 
325 	for (i = 0; i < 8; ++i)
326 	{
327 		narrow[i] = W_CachePatchName(va("NARROW%d", i+1), PU_HUDGFX);
328 		nredar[i] = W_CachePatchName(va("NREDAR%d", i+1), PU_HUDGFX);
329 	}
330 
331 	// non-animated version
332 	narrow[8] = W_CachePatchName("NARROW9", PU_HUDGFX);
333 
334 	drillbar = W_CachePatchName("DRILLBAR", PU_HUDGFX);
335 	for (i = 0; i < 3; ++i)
336 		drillfill[i] = W_CachePatchName(va("DRILLFI%d", i+1), PU_HUDGFX);
337 	capsulebar = W_CachePatchName("CAPSBAR", PU_HUDGFX);
338 	capsulefill = W_CachePatchName("CAPSFILL", PU_HUDGFX);
339 	minus5sec = W_CachePatchName("MINUS5", PU_HUDGFX);
340 
341 	for (i = 0; i < 7; ++i)
342 		ngradeletters[i] = W_CachePatchName(va("GRADE%d", i), PU_HUDGFX);
343 }
344 
345 // made separate so that skins code can reload custom face graphics
ST_LoadFaceGraphics(INT32 skinnum)346 void ST_LoadFaceGraphics(INT32 skinnum)
347 {
348 	if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC)
349 	{
350 		spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
351 		spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC];
352 		faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX);
353 		if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC)
354 		{
355 			sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER];
356 			sprframe = &sprdef->spriteframes[0];
357 			superprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX);
358 		}
359 		else
360 			superprefix[skinnum] = faceprefix[skinnum]; // not manually freed, okay to set to same pointer
361 	}
362 	else
363 		faceprefix[skinnum] = superprefix[skinnum] = W_CachePatchName("MISSING", PU_HUDGFX); // ditto
364 	facefreed[skinnum] = false;
365 }
366 
ST_ReloadSkinFaceGraphics(void)367 void ST_ReloadSkinFaceGraphics(void)
368 {
369 	INT32 i;
370 
371 	for (i = 0; i < numskins; i++)
372 		ST_LoadFaceGraphics(i);
373 }
374 
ST_InitData(void)375 static inline void ST_InitData(void)
376 {
377 	// 'link' the statusbar display to a player, which could be
378 	// another player than consoleplayer, for example, when you
379 	// change the view in a multiplayer demo with F12.
380 	stplyr = &players[displayplayer];
381 
382 	st_palette = -1;
383 }
384 
ST_Stop(void)385 static inline void ST_Stop(void)
386 {
387 	if (st_stopped)
388 		return;
389 
390 	V_SetPalette(0);
391 
392 	st_stopped = true;
393 }
394 
ST_Start(void)395 void ST_Start(void)
396 {
397 	if (!st_stopped)
398 		ST_Stop();
399 
400 	ST_InitData();
401 	st_stopped = false;
402 }
403 
404 //
405 // Initializes the status bar, sets the defaults border patch for the window borders.
406 //
407 
408 // used by OpenGL mode, holds lumpnum of flat used to fill space around the viewwindow
409 lumpnum_t st_borderpatchnum;
410 
ST_Init(void)411 void ST_Init(void)
412 {
413 	INT32 i;
414 
415 	for (i = 0; i < MAXPLAYERS; i++)
416 		facefreed[i] = true;
417 
418 	if (dedicated)
419 		return;
420 
421 	ST_LoadGraphics();
422 }
423 
424 // change the status bar too, when pressing F12 while viewing a demo.
ST_changeDemoView(void)425 void ST_changeDemoView(void)
426 {
427 	// the same routine is called at multiplayer deathmatch spawn
428 	// so it can be called multiple times
429 	ST_Start();
430 }
431 
432 // =========================================================================
433 //                         STATUS BAR OVERLAY
434 // =========================================================================
435 
436 boolean st_overlay;
437 
438 // =========================================================================
439 //                          INTERNAL DRAWING
440 // =========================================================================
441 #define ST_DrawTopLeftOverlayPatch(x,y,p)         V_DrawScaledPatch(x, y, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, p)
442 #define ST_DrawNumFromHud(h,n,flags)        V_DrawTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, n)
443 #define ST_DrawPadNumFromHud(h,n,q,flags)   V_DrawPaddedTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, n, q)
444 #define ST_DrawPatchFromHud(h,p,flags)      V_DrawScaledPatch(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, p)
445 
446 // Draw a number, scaled, over the view, maybe with set translucency
447 // Always draw the number completely since it's overlay
448 //
449 // Supports different colors! woo!
ST_DrawNightsOverlayNum(fixed_t x,fixed_t y,fixed_t s,INT32 a,UINT32 num,patch_t ** numpat,skincolornum_t colornum)450 static void ST_DrawNightsOverlayNum(fixed_t x /* right border */, fixed_t y, fixed_t s, INT32 a,
451 	UINT32 num, patch_t **numpat, skincolornum_t colornum)
452 {
453 	fixed_t w = numpat[0]->width * s;
454 	const UINT8 *colormap;
455 
456 	// I want my V_SNAPTOx flags. :< -Red
457 	//a &= V_ALPHAMASK;
458 
459 	if (colornum == 0)
460 		colormap = colormaps;
461 	else // Uses the player colors.
462 		colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE);
463 
464 	//I_Assert(num >= 0); // this function does not draw negative numbers
465 
466 	// draw the number
467 	do
468 	{
469 		x -= w;
470 		V_DrawFixedPatch(x, y, s, a, numpat[num % 10], colormap);
471 		num /= 10;
472 	} while (num);
473 
474 	// Sorry chum, this function only draws UNSIGNED values!
475 	// then why is num not UINT32? ~toast
476 }
477 
478 // Devmode information
ST_drawDebugInfo(void)479 static void ST_drawDebugInfo(void)
480 {
481 	INT32 height = 0, h = 8, w = 18, lowh;
482 	void (*textfunc)(INT32, INT32, INT32, const char *);
483 
484 	if (!(stplyr->mo && cv_debug))
485 		return;
486 
487 #define VFLAGS V_MONOSPACE|V_SNAPTOTOP|V_SNAPTORIGHT
488 
489 	if ((moviemode == MM_GIF && cv_gif_downscale.value) || vid.dupx == 1)
490 	{
491 		textfunc = V_DrawRightAlignedString;
492 		lowh = ((vid.height/vid.dupy) - 16);
493 	}
494 	else
495 	{
496 		textfunc = V_DrawRightAlignedSmallString;
497 		h /= 2;
498 		w /= 2;
499 		lowh = 0;
500 	}
501 
502 #define V_DrawDebugLine(str) if (lowh && (height > lowh))\
503 							{\
504 								V_DrawRightAlignedThinString(320,  8+lowh, VFLAGS|V_REDMAP, "SOME INFO NOT VISIBLE");\
505 								return;\
506 							}\
507 							textfunc(320, height, VFLAGS, str);\
508 							height += h;
509 
510 #define V_DrawDebugFlag(f, str) textfunc(width, height, VFLAGS|f, str);\
511 								width -= w
512 
513 	if (cv_debug & DBG_MEMORY)
514 	{
515 		V_DrawDebugLine(va("Heap: %8sKB", sizeu1(Z_TotalUsage()>>10)));
516 
517 		height += h/2;
518 	}
519 
520 	if (cv_debug & DBG_RANDOMIZER) // randomizer testing
521 	{
522 		fixed_t peekres = P_RandomPeek();
523 		peekres *= 10000;     // Change from fixed point
524 		peekres >>= FRACBITS; // to displayable decimal
525 
526 		V_DrawDebugLine(va("Init: %08x", P_GetInitSeed()));
527 		V_DrawDebugLine(va("Seed: %08x", P_GetRandSeed()));
528 		V_DrawDebugLine(va("==  :    .%04d", peekres));
529 
530 		height += h/2;
531 	}
532 
533 	if (cv_debug & DBG_PLAYER)
534 	{
535 		INT32 width = 320;
536 		const fixed_t d = AngleFixed(stplyr->drawangle);
537 
538 		V_DrawDebugLine(va("SHIELD: %5x", stplyr->powers[pw_shield]));
539 		V_DrawDebugLine(va("SCALE: %5d%%", (stplyr->mo->scale*100)>>FRACBITS));
540 		V_DrawDebugLine(va("CARRY: %5x", stplyr->powers[pw_carry]));
541 		V_DrawDebugLine(va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime]));
542 		V_DrawDebugLine(va("ABILITY: %3d, %3d", stplyr->charability, stplyr->charability2));
543 		V_DrawDebugLine(va("ACTIONSPD: %5d", stplyr->actionspd>>FRACBITS));
544 		V_DrawDebugLine(va("PEEL: %3d", stplyr->dashmode));
545 		V_DrawDebugLine(va("SCOREADD: %3d", stplyr->scoreadd));
546 
547 		// Flags
548 		V_DrawDebugFlag(((stplyr->pflags & PF_SHIELDABILITY)  ? V_GREENMAP : V_REDMAP), "SH");
549 		V_DrawDebugFlag(((stplyr->pflags & PF_THOKKED)        ? V_GREENMAP : V_REDMAP), "TH");
550 		V_DrawDebugFlag(((stplyr->pflags & PF_STARTDASH)      ? V_GREENMAP : V_REDMAP), "ST");
551 		V_DrawDebugFlag(((stplyr->pflags & PF_SPINNING)       ? V_GREENMAP : V_REDMAP), "SP");
552 		V_DrawDebugFlag(((stplyr->pflags & PF_NOJUMPDAMAGE)   ? V_GREENMAP : V_REDMAP), "ND");
553 		V_DrawDebugFlag(((stplyr->pflags & PF_JUMPED)         ? V_GREENMAP : V_REDMAP), "JD");
554 		V_DrawDebugFlag(((stplyr->pflags & PF_STARTJUMP)      ? V_GREENMAP : V_REDMAP), "SJ");
555 		V_DrawDebugFlag(0, "PF/SF:");
556 		height += h;
557 		width = 320;
558 		V_DrawDebugFlag(((stplyr->pflags & PF_INVIS)          ? V_GREENMAP : V_REDMAP), "*I");
559 		V_DrawDebugFlag(((stplyr->pflags & PF_NOCLIP)         ? V_GREENMAP : V_REDMAP), "*C");
560 		V_DrawDebugFlag(((stplyr->pflags & PF_GODMODE)        ? V_GREENMAP : V_REDMAP), "*G");
561 		V_DrawDebugFlag(((stplyr->charflags & SF_SUPER)       ? V_GREENMAP : V_REDMAP), "SU");
562 		V_DrawDebugFlag(((stplyr->pflags & PF_APPLYAUTOBRAKE) ? V_GREENMAP : V_REDMAP), "AA");
563 		V_DrawDebugFlag(((stplyr->pflags & PF_SLIDING)        ? V_GREENMAP : V_REDMAP), "SL");
564 		V_DrawDebugFlag(((stplyr->pflags & PF_BOUNCING)       ? V_GREENMAP : V_REDMAP), "BO");
565 		V_DrawDebugFlag(((stplyr->pflags & PF_GLIDING)        ? V_GREENMAP : V_REDMAP), "GL");
566 		height += h;
567 
568 		V_DrawDebugLine(va("DRAWANGLE: %6d", FixedInt(d)));
569 
570 		height += h/2;
571 	}
572 
573 	if (cv_debug & DBG_DETAILED)
574 	{
575 		INT32 width = 320;
576 
577 		V_DrawDebugLine(va("CEILINGZ: %6d", stplyr->mo->ceilingz>>FRACBITS));
578 		V_DrawDebugLine(va("FLOORZ: %6d", stplyr->mo->floorz>>FRACBITS));
579 
580 		V_DrawDebugLine(va("CMOMX: %6d", stplyr->cmomx>>FRACBITS));
581 		V_DrawDebugLine(va("CMOMY: %6d", stplyr->cmomy>>FRACBITS));
582 		V_DrawDebugLine(va("PMOMZ: %6d", stplyr->mo->pmomz>>FRACBITS));
583 
584 		width = 320;
585 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_APPLYPMOMZ)      ? V_GREENMAP : V_REDMAP), "AP");
586 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_SPRUNG)          ? V_GREENMAP : V_REDMAP), "SP");
587 		//V_DrawDebugFlag(((stplyr->mo->eflags & MFE_PUSHED)          ? V_GREENMAP : V_REDMAP), "PU"); -- not relevant to players
588 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_GOOWATER)        ? V_GREENMAP : V_REDMAP), "GW");
589 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_VERTICALFLIP)    ? V_GREENMAP : V_REDMAP), "VF");
590 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_JUSTSTEPPEDDOWN) ? V_GREENMAP : V_REDMAP), "JS");
591 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_UNDERWATER)      ? V_GREENMAP : V_REDMAP), "UW");
592 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_TOUCHWATER)      ? V_GREENMAP : V_REDMAP), "TW");
593 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_JUSTHITFLOOR)    ? V_GREENMAP : V_REDMAP), "JH");
594 		V_DrawDebugFlag(((stplyr->mo->eflags & MFE_ONGROUND)        ? V_GREENMAP : V_REDMAP), "OG");
595 		V_DrawDebugFlag(0, "MFE:");
596 		height += h;
597 
598 		V_DrawDebugLine(va("MOMX: %6d", stplyr->rmomx>>FRACBITS));
599 		V_DrawDebugLine(va("MOMY: %6d", stplyr->rmomy>>FRACBITS));
600 		V_DrawDebugLine(va("MOMZ: %6d", stplyr->mo->momz>>FRACBITS));
601 
602 		V_DrawDebugLine(va("SPEED: %6d", stplyr->speed>>FRACBITS));
603 
604 		height += h/2;
605 	}
606 
607 	if (cv_debug & DBG_BASIC)
608 	{
609 		const fixed_t d = AngleFixed(stplyr->mo->angle);
610 		V_DrawDebugLine(va("X: %6d", stplyr->mo->x>>FRACBITS));
611 		V_DrawDebugLine(va("Y: %6d", stplyr->mo->y>>FRACBITS));
612 		V_DrawDebugLine(va("Z: %6d", stplyr->mo->z>>FRACBITS));
613 		V_DrawDebugLine(va("A: %6d", FixedInt(d)));
614 
615 		//height += h/2;
616 	}
617 
618 #undef V_DrawDebugFlag
619 #undef V_DrawDebugLine
620 #undef VFLAGS
621 }
622 
ST_drawScore(void)623 static void ST_drawScore(void)
624 {
625 	if (F_GetPromptHideHud(hudinfo[HUD_SCORE].y))
626 		return;
627 
628 	// SCORE:
629 	ST_DrawPatchFromHud(HUD_SCORE, sboscore, V_HUDTRANS);
630 	if (objectplacing)
631 	{
632 		if (op_displayflags > UINT16_MAX)
633 			ST_DrawTopLeftOverlayPatch((hudinfo[HUD_SCORENUM].x-tallminus->width), hudinfo[HUD_SCORENUM].y, tallminus);
634 		else
635 			ST_DrawNumFromHud(HUD_SCORENUM, op_displayflags, V_HUDTRANS);
636 	}
637 	else
638 		ST_DrawNumFromHud(HUD_SCORENUM, stplyr->score, V_HUDTRANS);
639 }
640 
ST_drawRaceNum(INT32 time)641 static void ST_drawRaceNum(INT32 time)
642 {
643 	INT32 height, bounce;
644 	patch_t *racenum;
645 
646 	time += TICRATE;
647 	height = ((3*BASEVIDHEIGHT)>>2) - 8;
648 	bounce = TICRATE - (1 + (time % TICRATE));
649 
650 	switch (time/TICRATE)
651 	{
652 		case 3:
653 			racenum = race3;
654 			break;
655 		case 2:
656 			racenum = race2;
657 			break;
658 		case 1:
659 			racenum = race1;
660 			break;
661 		default:
662 			racenum = racego;
663 			break;
664 	}
665 	if (bounce < 3)
666 	{
667 		height -= (2 - bounce);
668 		if (!(P_AutoPause() || paused) && !bounce)
669 				S_StartSound(0, ((racenum == racego) ? sfx_s3kad : sfx_s3ka7));
670 	}
671 	V_DrawScaledPatch(((BASEVIDWIDTH - racenum->width)/2), height, V_PERPLAYER, racenum);
672 }
673 
ST_drawTime(void)674 static void ST_drawTime(void)
675 {
676 	INT32 seconds, minutes, tictrn, tics;
677 	boolean downwards = false;
678 
679 	if (objectplacing)
680 	{
681 		tics    = objectsdrawn;
682 		seconds = objectsdrawn%100;
683 		minutes = objectsdrawn/100;
684 		tictrn  = 0;
685 	}
686 	else
687 	{
688 		// Counting down the hidetime?
689 		if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime <= (hidetime*TICRATE)))
690 		{
691 			tics = (hidetime*TICRATE - stplyr->realtime);
692 			if (tics < 3*TICRATE)
693 				ST_drawRaceNum(tics);
694 			tics += (TICRATE-1); // match the race num
695 			downwards = true;
696 		}
697 		else
698 		{
699 			// Hidetime finish!
700 			if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
701 				ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
702 
703 			// Time limit?
704 			if ((gametyperules & GTR_TIMELIMIT) && cv_timelimit.value && timelimitintics > 0)
705 			{
706 				if (timelimitintics > stplyr->realtime)
707 				{
708 					tics = (timelimitintics - stplyr->realtime);
709 					if (tics < 3*TICRATE)
710 						ST_drawRaceNum(tics);
711 					tics += (TICRATE-1); // match the race num
712 				}
713 				else // Overtime!
714 					tics = 0;
715 				downwards = true;
716 			}
717 			// Post-hidetime normal.
718 			else if (gametyperules & GTR_STARTCOUNTDOWN)
719 				tics = stplyr->realtime - hidetime*TICRATE;
720 			// "Shadow! What are you doing? Hurry and get back here
721 			// right now before the island blows up with you on it!"
722 			// "Blows up??" *awkward silence* "I've got to get outta
723 			// here and find Amy and Tails right away!"
724 			else if (mapheaderinfo[gamemap-1]->countdown)
725 			{
726 				tics = countdowntimer;
727 				downwards = true;
728 			}
729 			// Normal.
730 			else
731 				tics = stplyr->realtime;
732 		}
733 
734 		minutes = G_TicsToMinutes(tics, true);
735 		seconds = G_TicsToSeconds(tics);
736 		tictrn  = G_TicsToCentiseconds(tics);
737 	}
738 
739 	if (F_GetPromptHideHud(hudinfo[HUD_TIME].y))
740 		return;
741 
742 	downwards = (downwards && (tics < 30*TICRATE) && (leveltime/5 & 1) && !stoppedclock); // overtime?
743 
744 	// TIME:
745 	ST_DrawPatchFromHud(HUD_TIME, (downwards ? sboredtime : sbotime), V_HUDTRANS);
746 
747 	if (downwards) // overtime!
748 		return;
749 
750 	if (cv_timetic.value == 3) // Tics only -- how simple is this?
751 		ST_DrawNumFromHud(HUD_SECONDS, tics, V_HUDTRANS);
752 	else
753 	{
754 		ST_DrawNumFromHud(HUD_MINUTES, minutes, V_HUDTRANS); // Minutes
755 		ST_DrawPatchFromHud(HUD_TIMECOLON, sbocolon, V_HUDTRANS); // Colon
756 		ST_DrawPadNumFromHud(HUD_SECONDS, seconds, 2, V_HUDTRANS); // Seconds
757 
758 		if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking || marathonmode)
759 		{
760 			ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod, V_HUDTRANS); // Period
761 			ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2, V_HUDTRANS); // Tics
762 		}
763 	}
764 }
765 
ST_drawRings(void)766 static inline void ST_drawRings(void)
767 {
768 	INT32 ringnum;
769 
770 	if (F_GetPromptHideHud(hudinfo[HUD_RINGS].y))
771 		return;
772 
773 	ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
774 
775 	if (objectplacing)
776 		ringnum = op_currentdoomednum;
777 	else if (stplyr->rings < 0 || stplyr->spectator || stplyr->playerstate == PST_REBORN)
778 		ringnum = 0;
779 	else
780 		ringnum = stplyr->rings;
781 
782 	if (cv_timetic.value == 2) // Yes, even in modeattacking
783 		ST_DrawNumFromHud(HUD_RINGSNUMTICS, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
784 	else
785 		ST_DrawNumFromHud(HUD_RINGSNUM, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
786 }
787 
ST_drawLivesArea(void)788 static void ST_drawLivesArea(void)
789 {
790 	INT32 v_colmap = V_YELLOWMAP, livescount;
791 	boolean notgreyedout;
792 
793 	if (!stplyr->skincolor)
794 		return; // Just joined a server, skin isn't loaded yet!
795 
796 	if (F_GetPromptHideHud(hudinfo[HUD_LIVES].y))
797 		return;
798 
799 	// face background
800 	V_DrawSmallScaledPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
801 		hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, livesback);
802 
803 	// face
804 	if (stplyr->spectator)
805 	{
806 		// spectator face
807 		UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, SKINCOLOR_CLOUDY, GTC_CACHE);
808 		V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
809 			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANSHALF, faceprefix[stplyr->skin], colormap);
810 	}
811 	else if (stplyr->mo && stplyr->mo->color)
812 	{
813 		// skincolor face/super
814 		UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->mo->color, GTC_CACHE);
815 		patch_t *face = faceprefix[stplyr->skin];
816 		if (stplyr->powers[pw_super] && !(stplyr->charflags & SF_NOSUPERSPRITES))
817 			face = superprefix[stplyr->skin];
818 		V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
819 			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, face, colormap);
820 		if (st_translucency == 10 && stplyr->powers[pw_super] == 1 && stplyr->mo->tracer)
821 		{
822 			INT32 v_supertrans = (stplyr->mo->tracer->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
823 			if (v_supertrans < 10)
824 			{
825 				v_supertrans <<= V_ALPHASHIFT;
826 				colormap = R_GetTranslationColormap(stplyr->skin, stplyr->mo->tracer->color, GTC_CACHE);
827 				V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
828 					hudinfo[HUD_LIVES].f|V_PERPLAYER|v_supertrans, face, colormap);
829 			}
830 		}
831 	}
832 	else if (stplyr->skincolor)
833 	{
834 		// skincolor face
835 		UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
836 		V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
837 			hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
838 	}
839 
840 	// Metal Sonic recording
841 	if (metalrecording)
842 	{
843 		if (((2*leveltime)/TICRATE) & 1)
844 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
845 				hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
846 	}
847 	// Spectator
848 	else if (stplyr->spectator)
849 		v_colmap = V_GRAYMAP;
850 	// Tag
851 	else if (gametyperules & GTR_TAG)
852 	{
853 		if (stplyr->pflags & PF_TAGIT)
854 		{
855 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "IT!");
856 			v_colmap = V_ORANGEMAP;
857 		}
858 	}
859 	// Team name
860 	else if (G_GametypeHasTeams())
861 	{
862 		if (stplyr->ctfteam == 1)
863 		{
864 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "RED");
865 			v_colmap = V_REDMAP;
866 		}
867 		else if (stplyr->ctfteam == 2)
868 		{
869 			V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "BLUE");
870 			v_colmap = V_BLUEMAP;
871 		}
872 	}
873 	// Lives number
874 	else
875 	{
876 		boolean candrawlives = true;
877 
878 		// Co-op and Competition, normal life counter
879 		if (G_GametypeUsesLives())
880 		{
881 			// Handle cooplives here
882 			if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 3)
883 			{
884 				INT32 i;
885 				livescount = 0;
886 				notgreyedout = (stplyr->lives > 0);
887 				for (i = 0; i < MAXPLAYERS; i++)
888 				{
889 					if (!playeringame[i])
890 						continue;
891 
892 					if (players[i].lives < 1)
893 						continue;
894 
895 					if (players[i].lives > 1)
896 						notgreyedout = true;
897 
898 					if (players[i].lives == INFLIVES)
899 					{
900 						livescount = INFLIVES;
901 						break;
902 					}
903 					else if (livescount < 99)
904 						livescount += (players[i].lives);
905 				}
906 			}
907 			else
908 			{
909 				livescount = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
910 				notgreyedout = true;
911 			}
912 		}
913 		// Infinity symbol (Race)
914 		else if (G_PlatformGametype() && !(gametyperules & GTR_LIVES))
915 		{
916 			livescount = INFLIVES;
917 			notgreyedout = true;
918 		}
919 		// Otherwise nothing, sorry.
920 		// Special Stages keep not showing lives,
921 		// as G_GametypeUsesLives() returns false in
922 		// Special Stages, and the infinity symbol
923 		// cannot show up because Special Stages
924 		// still have the GTR_LIVES gametype rule
925 		// by default.
926 		else
927 			candrawlives = false;
928 
929 		// Draw the lives counter here.
930 		if (candrawlives)
931 		{
932 			// x
933 			V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
934 			if (livescount == INFLIVES)
935 				V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
936 					'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
937 			else
938 			{
939 				if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)) && !(stplyr->pflags & PF_FINISHED))
940 					livescount++;
941 				if (livescount > 99)
942 					livescount = 99;
943 				V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
944 					hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
945 			}
946 		}
947 #undef ST_drawLivesX
948 	}
949 
950 	// name
951 	v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER);
952 	if (strlen(skins[stplyr->skin].hudname) <= 5)
953 		V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
954 	else if (V_StringWidth(skins[stplyr->skin].hudname, v_colmap) <= 48)
955 		V_DrawString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
956 	else if (V_ThinStringWidth(skins[stplyr->skin].hudname, v_colmap) <= 40)
957 		V_DrawRightAlignedThinString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
958 	else
959 		V_DrawThinString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
960 
961 	// Power Stones collected
962 	if (G_RingSlingerGametype() && LUA_HudEnabled(hud_powerstones))
963 	{
964 		INT32 workx = hudinfo[HUD_LIVES].x+1, j;
965 		if ((leveltime & 1) && stplyr->powers[pw_invulnerability] && (stplyr->powers[pw_sneakers] == stplyr->powers[pw_invulnerability])) // hack; extremely unlikely to be activated unintentionally
966 		{
967 			for (j = 0; j < 7; ++j) // "super" indicator
968 			{
969 				V_DrawScaledPatch(workx, hudinfo[HUD_LIVES].y-9, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, emeraldpics[1][j]);
970 				workx += 8;
971 			}
972 		}
973 		else
974 		{
975 			for (j = 0; j < 7; ++j) // powerstones
976 			{
977 				if (stplyr->powers[pw_emeralds] & (1 << j))
978 					V_DrawScaledPatch(workx, hudinfo[HUD_LIVES].y-9, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, emeraldpics[1][j]);
979 				workx += 8;
980 			}
981 		}
982 	}
983 }
984 
ST_drawInput(void)985 static void ST_drawInput(void)
986 {
987 	const INT32 accent = V_SNAPTOLEFT|V_SNAPTOBOTTOM|(stplyr->skincolor ? skincolors[stplyr->skincolor].ramp[4] : 0);
988 	INT32 col;
989 	UINT8 offs;
990 
991 	INT32 x = hudinfo[HUD_LIVES].x, y = hudinfo[HUD_LIVES].y;
992 
993 	if (stplyr->powers[pw_carry] == CR_NIGHTSMODE)
994 		y -= 16;
995 
996 	if (F_GetPromptHideHud(y))
997 		return;
998 
999 	// O backing
1000 	V_DrawFill(x, y-1, 16, 16, hudinfo[HUD_LIVES].f|20);
1001 	V_DrawFill(x, y+15, 16, 1, hudinfo[HUD_LIVES].f|29);
1002 
1003 	if (cv_showinputjoy.value) // joystick render!
1004 	{
1005 		/*V_DrawFill(x   , y   , 16,  1, hudinfo[HUD_LIVES].f|16);
1006 		V_DrawFill(x   , y+15, 16,  1, hudinfo[HUD_LIVES].f|16);
1007 		V_DrawFill(x   , y+ 1,  1, 14, hudinfo[HUD_LIVES].f|16);
1008 		V_DrawFill(x+15, y+ 1,  1, 14, hudinfo[HUD_LIVES].f|16); -- red's outline*/
1009 		if (stplyr->cmd.sidemove || stplyr->cmd.forwardmove)
1010 		{
1011 			// joystick hole
1012 			V_DrawFill(x+5, y+4, 6, 6, hudinfo[HUD_LIVES].f|29);
1013 			// joystick top
1014 			V_DrawFill(x+3+stplyr->cmd.sidemove/12,
1015 				y+2-stplyr->cmd.forwardmove/12,
1016 				10, 10, hudinfo[HUD_LIVES].f|29);
1017 			V_DrawFill(x+3+stplyr->cmd.sidemove/9,
1018 				y+1-stplyr->cmd.forwardmove/9,
1019 				10, 10, accent);
1020 		}
1021 		else
1022 		{
1023 			// just a limited, greyed out joystick top
1024 			V_DrawFill(x+3, y+11, 10, 1, hudinfo[HUD_LIVES].f|29);
1025 			V_DrawFill(x+3,
1026 				y+1,
1027 				10, 10, hudinfo[HUD_LIVES].f|16);
1028 		}
1029 	}
1030 	else // arrows!
1031 	{
1032 		// <
1033 		if (stplyr->cmd.sidemove < 0)
1034 		{
1035 			offs = 0;
1036 			col = accent;
1037 		}
1038 		else
1039 		{
1040 			offs = 1;
1041 			col = hudinfo[HUD_LIVES].f|16;
1042 			V_DrawFill(x- 2, y+10,  6,  1, hudinfo[HUD_LIVES].f|29);
1043 			V_DrawFill(x+ 4, y+ 9,  1,  1, hudinfo[HUD_LIVES].f|29);
1044 			V_DrawFill(x+ 5, y+ 8,  1,  1, hudinfo[HUD_LIVES].f|29);
1045 		}
1046 		V_DrawFill(x- 2, y+ 5-offs,  6,  6, col);
1047 		V_DrawFill(x+ 4, y+ 6-offs,  1,  4, col);
1048 		V_DrawFill(x+ 5, y+ 7-offs,  1,  2, col);
1049 
1050 		// ^
1051 		if (stplyr->cmd.forwardmove > 0)
1052 		{
1053 			offs = 0;
1054 			col = accent;
1055 		}
1056 		else
1057 		{
1058 			offs = 1;
1059 			col = hudinfo[HUD_LIVES].f|16;
1060 			V_DrawFill(x+ 5, y+ 3,  1,  1, hudinfo[HUD_LIVES].f|29);
1061 			V_DrawFill(x+ 6, y+ 4,  1,  1, hudinfo[HUD_LIVES].f|29);
1062 			V_DrawFill(x+ 7, y+ 5,  2,  1, hudinfo[HUD_LIVES].f|29);
1063 			V_DrawFill(x+ 9, y+ 4,  1,  1, hudinfo[HUD_LIVES].f|29);
1064 			V_DrawFill(x+10, y+ 3,  1,  1, hudinfo[HUD_LIVES].f|29);
1065 		}
1066 		V_DrawFill(x+ 5, y- 2-offs,  6,  6, col);
1067 		V_DrawFill(x+ 6, y+ 4-offs,  4,  1, col);
1068 		V_DrawFill(x+ 7, y+ 5-offs,  2,  1, col);
1069 
1070 		// >
1071 		if (stplyr->cmd.sidemove > 0)
1072 		{
1073 			offs = 0;
1074 			col = accent;
1075 		}
1076 		else
1077 		{
1078 			offs = 1;
1079 			col = hudinfo[HUD_LIVES].f|16;
1080 			V_DrawFill(x+12, y+10,  6,  1, hudinfo[HUD_LIVES].f|29);
1081 			V_DrawFill(x+11, y+ 9,  1,  1, hudinfo[HUD_LIVES].f|29);
1082 			V_DrawFill(x+10, y+ 8,  1,  1, hudinfo[HUD_LIVES].f|29);
1083 		}
1084 		V_DrawFill(x+12, y+ 5-offs,  6,  6, col);
1085 		V_DrawFill(x+11, y+ 6-offs,  1,  4, col);
1086 		V_DrawFill(x+10, y+ 7-offs,  1,  2, col);
1087 
1088 		// v
1089 		if (stplyr->cmd.forwardmove < 0)
1090 		{
1091 			offs = 0;
1092 			col = accent;
1093 		}
1094 		else
1095 		{
1096 			offs = 1;
1097 			col = hudinfo[HUD_LIVES].f|16;
1098 			V_DrawFill(x+ 5, y+17,  6,  1, hudinfo[HUD_LIVES].f|29);
1099 		}
1100 		V_DrawFill(x+ 5, y+12-offs,  6,  6, col);
1101 		V_DrawFill(x+ 6, y+11-offs,  4,  1, col);
1102 		V_DrawFill(x+ 7, y+10-offs,  2,  1, col);
1103 	}
1104 
1105 #define drawbutt(xoffs, yoffs, butt, symb)\
1106 	if (stplyr->cmd.buttons & butt)\
1107 	{\
1108 		offs = 0;\
1109 		col = accent;\
1110 	}\
1111 	else\
1112 	{\
1113 		offs = 1;\
1114 		col = hudinfo[HUD_LIVES].f|16;\
1115 		V_DrawFill(x+16+(xoffs), y+9+(yoffs), 10, 1, hudinfo[HUD_LIVES].f|29);\
1116 	}\
1117 	V_DrawFill(x+16+(xoffs), y+(yoffs)-offs, 10, 10, col);\
1118 	V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_LIVES].f|symb, false)
1119 
1120 	drawbutt( 4,-3, BT_JUMP, 'J');
1121 	drawbutt(15,-3, BT_SPIN, 'S');
1122 
1123 	V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_LIVES].f|20); // sundial backing
1124 	if (stplyr->mo)
1125 	{
1126 		UINT8 i, precision;
1127 		angle_t ang = (stplyr->powers[pw_carry] == CR_NIGHTSMODE)
1128 		? (FixedAngle((stplyr->flyangle-90)<<FRACBITS)>>ANGLETOFINESHIFT)
1129 		: (stplyr->mo->angle - R_PointToAngle(stplyr->mo->x, stplyr->mo->y))>>ANGLETOFINESHIFT;
1130 		fixed_t xcomp = FINESINE(ang)>>13;
1131 		fixed_t ycomp = FINECOSINE(ang)>>14;
1132 		if (ycomp == 4)
1133 			ycomp = 3;
1134 
1135 		if (ycomp > 0)
1136 			V_DrawFill(x+16+13-xcomp, y+11-ycomp, 3, 3, accent); // point (behind)
1137 
1138 		precision = max(3, abs(xcomp));
1139 		for (i = 0; i < precision; i++) // line
1140 		{
1141 			V_DrawFill(x+16+14-(i*xcomp)/precision,
1142 				y+12-(i*ycomp)/precision,
1143 				1, 1, hudinfo[HUD_LIVES].f|16);
1144 		}
1145 
1146 		if (ycomp <= 0)
1147 			V_DrawFill(x+16+13-xcomp, y+11-ycomp, 3, 3, accent); // point (in front)
1148 	}
1149 
1150 #undef drawbutt
1151 
1152 	// text above
1153 	x -= 2;
1154 	y -= 13;
1155 	if (stplyr->powers[pw_carry] != CR_NIGHTSMODE)
1156 	{
1157 		if (stplyr->pflags & PF_AUTOBRAKE)
1158 		{
1159 			V_DrawThinString(x, y,
1160 				hudinfo[HUD_LIVES].f|
1161 				((!stplyr->powers[pw_carry]
1162 				&& (stplyr->pflags & PF_APPLYAUTOBRAKE)
1163 				&& !(stplyr->cmd.sidemove || stplyr->cmd.forwardmove)
1164 				&& (stplyr->rmomx || stplyr->rmomy)
1165 				&& (!stplyr->capsule || (stplyr->capsule->reactiontime != (stplyr-players)+1)))
1166 				? 0 : V_GRAYMAP),
1167 				"AUTOBRAKE");
1168 			y -= 8;
1169 		}
1170 		switch (P_ControlStyle(stplyr))
1171 		{
1172 		case CS_LMAOGALOG:
1173 			V_DrawThinString(x, y, hudinfo[HUD_LIVES].f, "ANALOG");
1174 			y -= 8;
1175 			break;
1176 
1177 		case CS_SIMPLE:
1178 			V_DrawThinString(x, y, hudinfo[HUD_LIVES].f, "SIMPLE");
1179 			y -= 8;
1180 			break;
1181 
1182 		default:
1183 			break;
1184 		}
1185 	}
1186 	if (!demosynced) // should always be last, so it doesn't push anything else around
1187 		V_DrawThinString(x, y, hudinfo[HUD_LIVES].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!");
1188 }
1189 
1190 static patch_t *lt_patches[3];
1191 static INT32 lt_scroll = 0;
1192 static INT32 lt_mom = 0;
1193 static INT32 lt_zigzag = 0;
1194 
1195 tic_t lt_ticker = 0, lt_lasttic = 0;
1196 tic_t lt_exitticker = 0, lt_endtime = 0;
1197 
1198 //
1199 // Load the graphics for the title card.
1200 // Don't let LJ see this
1201 //
ST_cacheLevelTitle(void)1202 static void ST_cacheLevelTitle(void)
1203 {
1204 #define SETPATCH(default, warning, custom, idx) \
1205 { \
1206 	lumpnum_t patlumpnum = LUMPERROR; \
1207 	if (mapheaderinfo[gamemap-1]->custom[0] != '\0') \
1208 	{ \
1209 		patlumpnum = W_CheckNumForName(mapheaderinfo[gamemap-1]->custom); \
1210 		if (patlumpnum != LUMPERROR) \
1211 			lt_patches[idx] = (patch_t *)W_CachePatchNum(patlumpnum, PU_HUDGFX); \
1212 	} \
1213 	if (patlumpnum == LUMPERROR) \
1214 	{ \
1215 		if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE)) \
1216 			lt_patches[idx] = (patch_t *)W_CachePatchName(default, PU_HUDGFX); \
1217 		else \
1218 			lt_patches[idx] = (patch_t *)W_CachePatchName(warning, PU_HUDGFX); \
1219 	} \
1220 }
1221 
1222 	SETPATCH("LTACTBLU", "LTACTRED", ltactdiamond, 0)
1223 	SETPATCH("LTZIGZAG", "LTZIGRED", ltzzpatch, 1)
1224 	SETPATCH("LTZZTEXT", "LTZZWARN", ltzztext, 2)
1225 
1226 #undef SETPATCH
1227 }
1228 
1229 //
1230 // Start the title card.
1231 //
ST_startTitleCard(void)1232 void ST_startTitleCard(void)
1233 {
1234 	// cache every HUD patch used
1235 	ST_cacheLevelTitle();
1236 
1237 	// initialize HUD variables
1238 	lt_ticker = lt_exitticker = lt_lasttic = 0;
1239 	lt_endtime = 2*TICRATE + (10*NEWTICRATERATIO);
1240 	lt_scroll = BASEVIDWIDTH * FRACUNIT;
1241 	lt_zigzag = -((lt_patches[1])->width * FRACUNIT);
1242 	lt_mom = 0;
1243 }
1244 
1245 //
1246 // What happens before drawing the title card.
1247 // Which is just setting the HUD translucency.
1248 //
ST_preDrawTitleCard(void)1249 void ST_preDrawTitleCard(void)
1250 {
1251 	if (!G_IsTitleCardAvailable())
1252 		return;
1253 
1254 	if (lt_ticker >= (lt_endtime + TICRATE))
1255 		return;
1256 
1257 	if (!lt_exitticker)
1258 		st_translucency = 0;
1259 	else
1260 		st_translucency = max(0, min((INT32)lt_exitticker-4, cv_translucenthud.value));
1261 }
1262 
1263 //
1264 // Run the title card.
1265 // Called from ST_Ticker.
1266 //
ST_runTitleCard(void)1267 void ST_runTitleCard(void)
1268 {
1269 	boolean run = !(paused || P_AutoPause());
1270 
1271 	if (!G_IsTitleCardAvailable())
1272 		return;
1273 
1274 	if (lt_ticker >= (lt_endtime + TICRATE))
1275 		return;
1276 
1277 	if (run || (lt_ticker < PRELEVELTIME))
1278 	{
1279 		// tick
1280 		lt_ticker++;
1281 		if (lt_ticker >= lt_endtime)
1282 			lt_exitticker++;
1283 
1284 		// scroll to screen (level title)
1285 		if (!lt_exitticker)
1286 		{
1287 			if (abs(lt_scroll) > FRACUNIT)
1288 				lt_scroll -= (lt_scroll>>2);
1289 			else
1290 				lt_scroll = 0;
1291 		}
1292 		// scroll away from screen (level title)
1293 		else
1294 		{
1295 			lt_mom -= FRACUNIT*6;
1296 			lt_scroll += lt_mom;
1297 		}
1298 
1299 		// scroll to screen (zigzag)
1300 		if (!lt_exitticker)
1301 		{
1302 			if (abs(lt_zigzag) > FRACUNIT)
1303 				lt_zigzag -= (lt_zigzag>>2);
1304 			else
1305 				lt_zigzag = 0;
1306 		}
1307 		// scroll away from screen (zigzag)
1308 		else
1309 			lt_zigzag += lt_mom;
1310 	}
1311 }
1312 
1313 //
1314 // Draw the title card itself.
1315 //
ST_drawTitleCard(void)1316 void ST_drawTitleCard(void)
1317 {
1318 	char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl;
1319 	char *subttl = mapheaderinfo[gamemap-1]->subttl;
1320 	UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
1321 	INT32 lvlttlxpos, ttlnumxpos, zonexpos;
1322 	INT32 subttlxpos = BASEVIDWIDTH/2;
1323 	INT32 ttlscroll = FixedInt(lt_scroll);
1324 	INT32 zzticker;
1325 	patch_t *actpat, *zigzag, *zztext;
1326 	UINT8 colornum;
1327 	const UINT8 *colormap;
1328 
1329 	if (players[consoleplayer].skincolor)
1330 		colornum = players[consoleplayer].skincolor;
1331 	else
1332 		colornum = cv_playercolor.value;
1333 
1334 	colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE);
1335 
1336 	if (!G_IsTitleCardAvailable())
1337 		return;
1338 
1339 	if (!LUA_HudEnabled(hud_stagetitle))
1340 		goto luahook;
1341 
1342 	if (lt_ticker >= (lt_endtime + TICRATE))
1343 		goto luahook;
1344 
1345 	if ((lt_ticker-lt_lasttic) > 1)
1346 		lt_ticker = lt_lasttic+1;
1347 
1348 	ST_cacheLevelTitle();
1349 	actpat = lt_patches[0];
1350 	zigzag = lt_patches[1];
1351 	zztext = lt_patches[2];
1352 
1353 	lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
1354 
1355 	if (actnum > 0)
1356 		lvlttlxpos -= V_LevelActNumWidth(actnum);
1357 
1358 	ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl);
1359 	zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("Zone"));
1360 	ttlnumxpos++;
1361 
1362 	if (lvlttlxpos < 0)
1363 		lvlttlxpos = 0;
1364 
1365 	if (!splitscreen || (splitscreen && stplyr == &players[displayplayer]))
1366 	{
1367 		zzticker = lt_ticker;
1368 		V_DrawMappedPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap);
1369 		V_DrawMappedPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap);
1370 		V_DrawMappedPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap);
1371 		V_DrawMappedPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap);
1372 	}
1373 
1374 	if (actnum)
1375 	{
1376 		if (!splitscreen)
1377 		{
1378 			if (actnum > 9) // slightly offset the act diamond for two-digit act numbers
1379 				V_DrawMappedPatch(ttlnumxpos + (V_LevelActNumWidth(actnum)/4) + ttlscroll, 104 - ttlscroll, 0, actpat, colormap);
1380 			else
1381 				V_DrawMappedPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat, colormap);
1382 		}
1383 		V_DrawLevelActNum(ttlnumxpos + ttlscroll, 104, V_PERPLAYER, actnum);
1384 	}
1385 
1386 	V_DrawLevelTitle(lvlttlxpos - ttlscroll, 80, V_PERPLAYER, lvlttl);
1387 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
1388 		V_DrawLevelTitle(zonexpos + ttlscroll, 104, V_PERPLAYER, M_GetText("Zone"));
1389 	V_DrawCenteredString(subttlxpos - ttlscroll, 135, V_PERPLAYER|V_ALLOWLOWERCASE, subttl);
1390 
1391 	lt_lasttic = lt_ticker;
1392 
1393 luahook:
1394 	LUAh_TitleCardHUD(stplyr);
1395 }
1396 
1397 //
1398 // Drawer for G_PreLevelTitleCard.
1399 //
ST_preLevelTitleCardDrawer(void)1400 void ST_preLevelTitleCardDrawer(void)
1401 {
1402 	V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
1403 	ST_drawWipeTitleCard();
1404 	I_OsPolling();
1405 	I_UpdateNoBlit();
1406 }
1407 
1408 //
1409 // Draw the title card while on a wipe.
1410 // Also used in G_PreLevelTitleCard.
1411 //
ST_drawWipeTitleCard(void)1412 void ST_drawWipeTitleCard(void)
1413 {
1414 	stplyr = &players[consoleplayer];
1415 	ST_preDrawTitleCard();
1416 	ST_drawTitleCard();
1417 	if (splitscreen)
1418 	{
1419 		stplyr = &players[secondarydisplayplayer];
1420 		ST_preDrawTitleCard();
1421 		ST_drawTitleCard();
1422 	}
1423 }
1424 
ST_drawPowerupHUD(void)1425 static void ST_drawPowerupHUD(void)
1426 {
1427 	patch_t *p = NULL;
1428 	UINT16 invulntime = 0;
1429 	INT32 offs = hudinfo[HUD_POWERUPS].x;
1430 	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
1431 	static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}, finishoffs[2] = {0, 0};
1432 #define ICONSEP (16+4) // matches weapon rings HUD
1433 
1434 	if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y))
1435 		return;
1436 
1437 	if (stplyr->spectator || stplyr->playerstate != PST_LIVE)
1438 		return;
1439 
1440 // ---------
1441 // Finish icon
1442 // ---------
1443 
1444 	// Let's have a power-like icon to represent finishing the level!
1445 	if (stplyr->pflags & PF_FINISHED && cv_exitmove.value && multiplayer)
1446 	{
1447 		finishoffs[q] = ICONSEP;
1448 		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, fnshico);
1449 	}
1450 	else if (finishoffs[q])
1451 	{
1452 		if (finishoffs[q] > 1)
1453 			finishoffs[q] = 2*finishoffs[q]/3;
1454 		else
1455 			finishoffs[q] = 0;
1456 	}
1457 
1458 	offs -= finishoffs[q];
1459 
1460 // -------
1461 // Shields
1462 // -------
1463 
1464 	// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
1465 	if (stplyr->powers[pw_shield] & SH_NOSTACK)
1466 	{
1467 		shieldoffs[q] = ICONSEP;
1468 
1469 		if ((stplyr->powers[pw_shield] & SH_NOSTACK & ~SH_FORCEHP) == SH_FORCE)
1470 		{
1471 			UINT8 i, max = (stplyr->powers[pw_shield] & SH_FORCEHP);
1472 			for (i = 0; i <= max; i++)
1473 			{
1474 				V_DrawSmallScaledPatch(offs-(i<<1), hudinfo[HUD_POWERUPS].y-(i<<1), (V_PERPLAYER|hudinfo[HUD_POWERUPS].f)|((i == max) ? V_HUDTRANS : V_HUDTRANSHALF), forceshield);
1475 			}
1476 		}
1477 		else
1478 		{
1479 			switch (stplyr->powers[pw_shield] & SH_NOSTACK)
1480 			{
1481 				case SH_WHIRLWIND:   p = jumpshield;    break;
1482 				case SH_ELEMENTAL:   p = watershield;   break;
1483 				case SH_ARMAGEDDON:  p = bombshield;    break;
1484 				case SH_ATTRACT:     p = ringshield;    break;
1485 				case SH_PITY:        p = pityshield;    break;
1486 				case SH_PINK:        p = pinkshield;    break;
1487 				case SH_FLAMEAURA:   p = flameshield;   break;
1488 				case SH_BUBBLEWRAP:  p = bubbleshield;  break;
1489 				case SH_THUNDERCOIN: p = thundershield; break;
1490 				default: break;
1491 			}
1492 
1493 			if (p)
1494 				V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, p);
1495 		}
1496 	}
1497 	else if (shieldoffs[q])
1498 	{
1499 		if (shieldoffs[q] > 1)
1500 			shieldoffs[q] = 2*shieldoffs[q]/3;
1501 		else
1502 			shieldoffs[q] = 0;
1503 	}
1504 
1505 	offs -= shieldoffs[q];
1506 
1507 // ---------
1508 // CTF flags
1509 // ---------
1510 
1511 	// YOU have a flag. Display a monitor-like icon for it.
1512 	if (stplyr->gotflag)
1513 	{
1514 		flagoffs[q] = ICONSEP;
1515 		p = (stplyr->gotflag & GF_REDFLAG) ? gotrflag : gotbflag;
1516 		V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, p);
1517 	}
1518 	else if (flagoffs[q])
1519 	{
1520 		if (flagoffs[q] > 1)
1521 			flagoffs[q] = 2*flagoffs[q]/3;
1522 		else
1523 			flagoffs[q] = 0;
1524 	}
1525 
1526 	offs -= flagoffs[q];
1527 
1528 // --------------------
1529 // Timer-based powerups
1530 // --------------------
1531 
1532 #define DRAWTIMERICON(patch, timer) \
1533 	V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, patch); \
1534 	V_DrawRightAlignedThinString(offs + 16, hudinfo[HUD_POWERUPS].y + 8, V_PERPLAYER|hudinfo[HUD_POWERUPS].f, va("%d", timer/TICRATE));
1535 
1536 	// Invincibility, both from monitor and after being hit
1537 	invulntime = stplyr->powers[pw_flashing] ? stplyr->powers[pw_flashing] : stplyr->powers[pw_invulnerability];
1538 	// Note: pw_flashing always makes the icon flicker regardless of time, unlike pw_invulnerability
1539 	if (stplyr->powers[pw_invulnerability] > 3*TICRATE || (invulntime && leveltime & 1))
1540 	{
1541 		DRAWTIMERICON(invincibility, invulntime)
1542 	}
1543 
1544 	if (invulntime > 7)
1545 		offs -= ICONSEP;
1546 	else
1547 	{
1548 		UINT8 a = ICONSEP, b = 7-invulntime;
1549 		while (b--)
1550 			a = 2*a/3;
1551 		offs -= a;
1552 	}
1553 
1554 	// Super Sneakers
1555 	if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1))
1556 	{
1557 		DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers])
1558 	}
1559 
1560 	if (stplyr->powers[pw_sneakers] > 7)
1561 		offs -= ICONSEP;
1562 	else
1563 	{
1564 		UINT8 a = ICONSEP, b = 7-stplyr->powers[pw_sneakers];
1565 		while (b--)
1566 			a = 2*a/3;
1567 		offs -= a;
1568 	}
1569 
1570 	// Gravity Boots
1571 	if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1))
1572 	{
1573 		DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots])
1574 	}
1575 
1576 #undef DRAWTIMERICON
1577 #undef ICONSEP
1578 }
1579 
ST_drawFirstPersonHUD(void)1580 static void ST_drawFirstPersonHUD(void)
1581 {
1582 	patch_t *p = NULL;
1583 	UINT32 airtime;
1584 	spriteframe_t *sprframe;
1585 	// If both air timers are active, use the air timer with the least time left
1586 	if (stplyr->powers[pw_underwater] && stplyr->powers[pw_spacetime])
1587 		airtime = min(stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime]);
1588 	else // Use whichever one is active otherwise
1589 		airtime = (stplyr->powers[pw_spacetime]) ? stplyr->powers[pw_spacetime] : stplyr->powers[pw_underwater];
1590 
1591 	if (airtime < 1)
1592 		return; // No air timers are active, nothing would be drawn anyway
1593 
1594 	airtime--; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity
1595 
1596 	if (airtime > 11*TICRATE)
1597 		return; // Not time to draw any drown numbers yet
1598 
1599 	if (!((airtime > 10*TICRATE - 5)
1600 	|| (airtime <= 9*TICRATE && airtime > 8*TICRATE - 5)
1601 	|| (airtime <= 7*TICRATE && airtime > 6*TICRATE - 5)
1602 	|| (airtime <= 5*TICRATE && airtime > 4*TICRATE - 5)
1603 	|| (airtime <= 3*TICRATE && airtime > 2*TICRATE - 5)
1604 	|| (airtime <= 1*TICRATE)))
1605 		return; // Don't draw anything between numbers
1606 
1607 	if (!((airtime % 10) < 5))
1608 		return; // Keep in line with the flashing thing from third person.
1609 
1610 	airtime /= (2*TICRATE); // To be strictly accurate it'd need to be ((airtime/TICRATE) - 1)/2, but integer division rounds down for us
1611 
1612 	if (stplyr->charflags & SF_MACHINE)
1613 		airtime += 6;  // Robots use different drown numbers
1614 
1615 	// Get the front angle patch for the frame
1616 	sprframe = &sprites[SPR_DRWN].spriteframes[airtime];
1617 	p = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
1618 
1619 	// Display the countdown drown numbers!
1620 	if (p && !F_GetPromptHideHud(60 - p->topoffset))
1621 		V_DrawScaledPatch((BASEVIDWIDTH/2) - (p->width / 2) + SHORT(p->leftoffset), 60 - SHORT(p->topoffset),
1622 			V_PERPLAYER|V_PERPLAYER|V_TRANSLUCENT, p);
1623 }
1624 
ST_drawNightsRecords(void)1625 static void ST_drawNightsRecords(void)
1626 {
1627 	INT32 aflag = V_PERPLAYER;
1628 
1629 	if (!stplyr->texttimer)
1630 		return;
1631 
1632 	if (stplyr->texttimer < TICRATE/2)
1633 		aflag |= (9 - 9*stplyr->texttimer/(TICRATE/2)) << V_ALPHASHIFT;
1634 
1635 	switch (stplyr->textvar)
1636 	{
1637 		case 1: // A "Bonus Time Start" by any other name...
1638 		{
1639 			V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!"));
1640 			V_DrawCenteredString(BASEVIDWIDTH/2, 60,            aflag, M_GetText("SCORE MULTIPLIER START!"));
1641 
1642 			if (stplyr->finishedtime)
1643 			{
1644 				V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:");
1645 				V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:");
1646 				V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE));
1647 				V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100));
1648 			}
1649 			break;
1650 		}
1651 		case 2: // Get n Spheres
1652 		case 3: // Get n more Spheres
1653 		{
1654 			if (!stplyr->capsule)
1655 				return;
1656 
1657 			// Yes, this string is an abomination.
1658 			V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
1659 								 va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health,
1660 									(stplyr->textvar == 3) ? M_GetText("MORE ") : "",
1661 									(G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP",
1662 									(stplyr->capsule->health > 1) ? "S" : ""));
1663 			break;
1664 		}
1665 		case 4: // End Bonus
1666 		{
1667 			V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:");
1668 			V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:");
1669 			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres));
1670 			V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50));
1671 			ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE);
1672 
1673 			// If new record, say so!
1674 			if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1) <= stplyr->lastmarescore)
1675 			{
1676 				if (stplyr->texttimer & 16)
1677 					V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *");
1678 			}
1679 
1680 			if (P_HasGrades(gamemap, stplyr->lastmare + 1))
1681 			{
1682 				UINT8 grade = P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare);
1683 				if (modeattacking || grade >= GRADE_A)
1684 					V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag, ngradeletters[grade]);
1685 			}
1686 			break;
1687 		}
1688 		default:
1689 			break;
1690 	}
1691 }
1692 
1693 // 2.0-1: [21:42] <+Rob> Beige - Lavender - Steel Blue - Peach - Orange - Purple - Silver - Yellow - Pink - Red - Blue - Green - Cyan - Gold
1694 /*#define NUMLINKCOLORS 14
1695 static skincolornum_t linkColor[NUMLINKCOLORS] =
1696 {SKINCOLOR_BEIGE,  SKINCOLOR_LAVENDER, SKINCOLOR_AZURE, SKINCOLOR_PEACH, SKINCOLOR_ORANGE,
1697  SKINCOLOR_MAGENTA, SKINCOLOR_SILVER, SKINCOLOR_SUPERGOLD4, SKINCOLOR_PINK,  SKINCOLOR_RED,
1698  SKINCOLOR_BLUE, SKINCOLOR_GREEN, SKINCOLOR_CYAN, SKINCOLOR_GOLD};*/
1699 
1700 // 2.2 indev list: (unix time 1470866042) <Rob> Emerald, Aqua, Cyan, Blue, Pastel, Purple, Magenta, Rosy, Red, Orange, Gold, Yellow, Peridot
1701 /*#define NUMLINKCOLORS 13
1702 static skincolornum_t linkColor[NUMLINKCOLORS] =
1703 {SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_CYAN, SKINCOLOR_BLUE, SKINCOLOR_PASTEL,
1704  SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA, SKINCOLOR_ROSY, SKINCOLOR_RED,  SKINCOLOR_ORANGE,
1705  SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT};*/
1706 
1707 // 2.2 indev list again: [19:59:52] <baldobo> Ruby > Red > Flame > Sunset > Orange > Gold > Yellow > Lime > Green > Aqua  > cyan > Sky > Blue > Pastel > Purple > Bubblegum > Magenta > Rosy > repeat
1708 // [20:00:25] <baldobo> Also Icy for the link freeze text color
1709 // [20:04:03] <baldobo> I would start it on lime
1710 /*#define NUMLINKCOLORS 18
1711 static skincolornum_t linkColor[NUMLINKCOLORS] =
1712 {SKINCOLOR_LIME, SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_CYAN, SKINCOLOR_SKY,
1713  SKINCOLOR_SAPPHIRE, SKINCOLOR_PASTEL, SKINCOLOR_PURPLE, SKINCOLOR_BUBBLEGUM, SKINCOLOR_MAGENTA,
1714  SKINCOLOR_ROSY, SKINCOLOR_RUBY, SKINCOLOR_RED, SKINCOLOR_FLAME, SKINCOLOR_SUNSET,
1715  SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW};*/
1716 
1717 // 2.2+ list for real this time: https://wiki.srb2.org/wiki/User:Rob/Sandbox (check history around 31/10/17, spoopy)
1718 #define NUMLINKCOLORS 12
1719 static skincolornum_t linkColor[2][NUMLINKCOLORS] = {
1720 {SKINCOLOR_EMERALD, SKINCOLOR_AQUA, SKINCOLOR_SKY, SKINCOLOR_BLUE, SKINCOLOR_PURPLE, SKINCOLOR_MAGENTA,
1721  SKINCOLOR_ROSY, SKINCOLOR_RED, SKINCOLOR_ORANGE, SKINCOLOR_GOLD, SKINCOLOR_YELLOW, SKINCOLOR_PERIDOT},
1722 {SKINCOLOR_SEAFOAM, SKINCOLOR_CYAN, SKINCOLOR_WAVE, SKINCOLOR_SAPPHIRE, SKINCOLOR_VAPOR, SKINCOLOR_BUBBLEGUM,
1723  SKINCOLOR_VIOLET, SKINCOLOR_RUBY, SKINCOLOR_FLAME, SKINCOLOR_SUNSET, SKINCOLOR_SANDY, SKINCOLOR_LIME}};
1724 
ST_drawNiGHTSLink(void)1725 static void ST_drawNiGHTSLink(void)
1726 {
1727 	static INT32 prevsel[2] = {0, 0}, prevtime[2] = {0, 0};
1728 	const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
1729 	INT32 sel = ((stplyr->linkcount-1) / 5) % NUMLINKCOLORS, aflag = V_PERPLAYER, mag = ((stplyr->linkcount-1 >= 300) ? 1 : 0);
1730 	skincolornum_t colornum;
1731 	fixed_t x, y, scale;
1732 
1733 	if (sel != prevsel[q])
1734 	{
1735 		prevsel[q] = sel;
1736 		prevtime[q] = 2 + mag;
1737 	}
1738 
1739 	if (stplyr->powers[pw_nights_linkfreeze] && (!(stplyr->powers[pw_nights_linkfreeze] & 2) || (stplyr->powers[pw_nights_linkfreeze] > flashingtics)))
1740 		colornum = SKINCOLOR_ICY;
1741 	else
1742 		colornum = linkColor[mag][sel];
1743 
1744 	aflag |= ((stplyr->linktimer < (UINT32)nightslinktics/3)
1745 	? (9 - 9*stplyr->linktimer/(nightslinktics/3)) << V_ALPHASHIFT
1746 	: 0);
1747 
1748 	y = (160+11)<<FRACBITS;
1749 	aflag |= V_SNAPTOBOTTOM;
1750 
1751 	x = (160+4)<<FRACBITS;
1752 
1753 	if (prevtime[q])
1754 	{
1755 		scale = ((32 + prevtime[q])<<FRACBITS)/32;
1756 		prevtime[q]--;
1757 	}
1758 	else
1759 		scale = FRACUNIT;
1760 
1761 	y -= (11*scale);
1762 
1763 	ST_DrawNightsOverlayNum(x-(4*scale), y, scale, aflag, (stplyr->linkcount-1), nightsnum, colornum);
1764 	V_DrawFixedPatch(x+(4*scale), y, scale, aflag, nightslink,
1765 		colornum == 0 ? colormaps : R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE));
1766 
1767 	// Show remaining link time left in debug
1768 	if (cv_debug & DBG_NIGHTSBASIC)
1769 		V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_SNAPTOBOTTOM, va("End in %d.%02d", stplyr->linktimer/TICRATE, G_TicsToCentiseconds(stplyr->linktimer)));
1770 }
1771 
1772 
ST_drawNiGHTSHUD(void)1773 static void ST_drawNiGHTSHUD(void)
1774 {
1775 	INT32 origamount;
1776 	INT32 total_spherecount, total_ringcount;
1777 	const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS));
1778 
1779 	// Drill meter
1780 	if (LUA_HudEnabled(hud_nightsdrill) && stplyr->powers[pw_carry] == CR_NIGHTSMODE)
1781 	{
1782 		INT32 locx = 16, locy = 180;
1783 		INT32 dfill;
1784 		UINT8 fillpatch;
1785 
1786 		// Use which patch?
1787 		if (stplyr->pflags & PF_DRILLING)
1788 			fillpatch = (stplyr->drillmeter & 1) + 1;
1789 		else
1790 			fillpatch = 0;
1791 
1792 		V_DrawScaledPatch(locx, locy, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS, drillbar);
1793 		for (dfill = 0; dfill < stplyr->drillmeter/20 && dfill < 96; ++dfill)
1794 			V_DrawScaledPatch(locx + 2 + dfill, locy + 3, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS, drillfill[fillpatch]);
1795 
1796 		// Display actual drill amount and bumper time
1797 		if (!splitscreen && (cv_debug & DBG_NIGHTSBASIC))
1798 		{
1799 			if (stplyr->bumpertime)
1800 				V_DrawString(locx, locy - 8, V_REDMAP|V_MONOSPACE, va("BUMPER: 0.%02d", G_TicsToCentiseconds(stplyr->bumpertime)));
1801 			else
1802 				V_DrawString(locx, locy - 8, V_MONOSPACE, va("Drill: %3d%%", (stplyr->drillmeter*100)/(96*20)));
1803 		}
1804 	}
1805 
1806 	/*if (G_IsSpecialStage(gamemap))
1807 	{ // Since special stages share score, time, rings, etc.
1808 		// disable splitscreen mode for its HUD.
1809 		// --------------------------------------
1810 		// NOPE! Consistency between different splitscreen stuffs
1811 		// now we've got the screen squashing instead. ~toast
1812 		if (stplyr != &players[displayplayer])
1813 			return;
1814 		nosshack = splitscreen;
1815 		splitscreen = false;
1816 	}*/
1817 
1818 	// Link drawing
1819 	if (!oldspecialstage
1820 	// Don't display when the score is showing (it popping up for a split second when exiting a map is intentional)
1821 	&& !(stplyr->texttimer && stplyr->textvar == 4)
1822 	&& LUA_HudEnabled(hud_nightslink)
1823 	&& ((cv_debug & DBG_NIGHTSBASIC) || stplyr->linkcount > 1)) // When debugging, show "0 Link".
1824 	{
1825 		ST_drawNiGHTSLink();
1826 	}
1827 
1828 	if (gametyperules & GTR_RACE)
1829 	{
1830 		ST_drawScore();
1831 		ST_drawTime();
1832 		return;
1833 	}
1834 
1835 	// Begin drawing brackets/chip display
1836 	if (LUA_HudEnabled(hud_nightsspheres))
1837 	{
1838 	ST_DrawTopLeftOverlayPatch(16, 8, nbracket);
1839 	if (G_IsSpecialStage(gamemap))
1840 		ST_DrawTopLeftOverlayPatch(24, 16, (
1841 			(stplyr->bonustime && (leveltime & 4) && (states[S_BLUESPHEREBONUS].frame & FF_ANIMATE)) ? nssbon : nsshud));
1842 	else
1843 		ST_DrawTopLeftOverlayPatch(24, 16, *(((stplyr->bonustime) ? nbon : nhud)+((leveltime/2)%12)));
1844 
1845 	if (G_IsSpecialStage(gamemap))
1846 	{
1847 		INT32 i;
1848 		total_spherecount = total_ringcount = 0;
1849 		for (i = 0; i < MAXPLAYERS; i++)
1850 		{
1851 			if (!playeringame[i])
1852 				continue;
1853 			total_spherecount += players[i].spheres;
1854 			total_ringcount += players[i].rings;
1855 		}
1856 	}
1857 	else
1858 	{
1859 		total_spherecount = stplyr->spheres;
1860 		total_ringcount = stplyr->spheres;
1861 	}
1862 
1863 	if (stplyr->capsule)
1864 	{
1865 		INT32 amount;
1866 		const INT32 length = 88;
1867 
1868 		origamount = stplyr->capsule->spawnpoint->angle;
1869 		I_Assert(origamount > 0); // should not happen now
1870 
1871 		ST_DrawTopLeftOverlayPatch(72, 8, nbracket);
1872 		ST_DrawTopLeftOverlayPatch(74, 8 + 4, minicaps);
1873 
1874 		if (stplyr->capsule->reactiontime != 0)
1875 		{
1876 			INT32 r;
1877 			const INT32 orblength = 20;
1878 
1879 			for (r = 0; r < 5; r++)
1880 			{
1881 				V_DrawScaledPatch(230 - (7*r), 144, V_PERPLAYER|V_HUDTRANS, redstat);
1882 				V_DrawScaledPatch(188 - (7*r), 144, V_PERPLAYER|V_HUDTRANS, orngstat);
1883 				V_DrawScaledPatch(146 - (7*r), 144, V_PERPLAYER|V_HUDTRANS, yelstat);
1884 				V_DrawScaledPatch(104 - (7*r), 144, V_PERPLAYER|V_HUDTRANS, byelstat);
1885 			}
1886 
1887 			amount = (origamount - stplyr->capsule->health);
1888 			amount = (amount * orblength)/origamount;
1889 
1890 			if (amount > 0)
1891 			{
1892 				INT32 t;
1893 
1894 				// Fill up the bar with blue orbs... in reverse! (yuck)
1895 				for (r = amount; r > 0; r--)
1896 				{
1897 					t = r;
1898 
1899 					if (r > 15) ++t;
1900 					if (r > 10) ++t;
1901 					if (r > 5)  ++t;
1902 
1903 					V_DrawScaledPatch(69 + (7*t), 144, V_PERPLAYER|V_HUDTRANS, bluestat);
1904 				}
1905 			}
1906 		}
1907 		else
1908 		{
1909 			INT32 cfill;
1910 
1911 			// Lil' white box!
1912 			V_DrawScaledPatch(15, 8 + 34, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulebar);
1913 
1914 			amount = (origamount - stplyr->capsule->health);
1915 			amount = (amount * length)/origamount;
1916 
1917 			for (cfill = 0; cfill < amount && cfill < length; ++cfill)
1918 				V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill);
1919 		}
1920 
1921 		if (total_spherecount >= stplyr->capsule->health)
1922 			ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime&7]);
1923 		else
1924 			ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]);
1925 	}
1926 	else if (oldspecialstage && total_spherecount < (INT32)ssspheres)
1927 	{
1928 		INT32 cfill, amount;
1929 		const INT32 length = 88;
1930 		UINT8 em = P_GetNextEmerald();
1931 		ST_DrawTopLeftOverlayPatch(72, 8, nbracket);
1932 
1933 		if (em <= 7)
1934 			ST_DrawTopLeftOverlayPatch(80, 8 + 8, emeraldpics[0][em]);
1935 
1936 		ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]);
1937 
1938 		// Lil' white box!
1939 		V_DrawScaledPatch(15, 8 + 34, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulebar);
1940 
1941 		amount = (total_spherecount * length)/ssspheres;
1942 
1943 		for (cfill = 0; cfill < amount && cfill < length; ++cfill)
1944 			V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill);
1945 	}
1946 	else
1947 		ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[8]);
1948 
1949 	if (oldspecialstage)
1950 	{
1951 		// invert for s3k style junk
1952 		total_spherecount = ssspheres - total_spherecount;
1953 		if (total_spherecount < 0)
1954 			total_spherecount = 0;
1955 
1956 		if (nummaprings > 0) // don't count down if there ISN'T a valid maximum number of rings, like sonic 3
1957 		{
1958 			total_ringcount = nummaprings - total_ringcount;
1959 			if (total_ringcount < 0)
1960 				total_ringcount = 0;
1961 		}
1962 
1963 		// now rings! you know, for that perfect bonus.
1964 		V_DrawScaledPatch(272, 8, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, nbracket);
1965 		V_DrawScaledPatch(280, 16+1, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, nring);
1966 		V_DrawScaledPatch(280, 8+5, V_FLIP|V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, narrow[8]);
1967 		V_DrawTallNum(272, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, total_ringcount);
1968 	}
1969 
1970 	if (total_spherecount >= 100)
1971 		V_DrawTallNum((total_spherecount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
1972 	else
1973 		V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
1974 	}
1975 
1976 	// Score
1977 	if (!stplyr->exiting && !oldspecialstage && LUA_HudEnabled(hud_nightsscore))
1978 		ST_DrawNightsOverlayNum(304<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT, stplyr->marescore, nightsnum, SKINCOLOR_AZURE);
1979 
1980 	// TODO give this its own section for Lua
1981 	if (!stplyr->exiting && LUA_HudEnabled(hud_nightsscore))
1982 	{
1983 		if (modeattacking == ATTACKING_NIGHTS)
1984 		{
1985 			INT32 maretime = max(stplyr->realtime - stplyr->marebegunat, 0);
1986 
1987 #define VFLAGS V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_PERPLAYER|V_HUDTRANS
1988 			V_DrawScaledPatch(BASEVIDWIDTH-22, BASEVIDHEIGHT-20, VFLAGS, W_CachePatchName("NGRTIMER", PU_HUDGFX));
1989 			V_DrawPaddedTallNum(BASEVIDWIDTH-22, BASEVIDHEIGHT-20, VFLAGS, G_TicsToCentiseconds(maretime), 2);
1990 			V_DrawScaledPatch(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, sboperiod);
1991 			if (maretime < 60*TICRATE)
1992 				V_DrawTallNum(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, G_TicsToSeconds(maretime));
1993 			else
1994 			{
1995 				V_DrawPaddedTallNum(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, G_TicsToSeconds(maretime), 2);
1996 				V_DrawScaledPatch(BASEVIDWIDTH-70, BASEVIDHEIGHT-20, VFLAGS, sbocolon);
1997 				V_DrawTallNum(BASEVIDWIDTH-70, BASEVIDHEIGHT-20, VFLAGS, G_TicsToMinutes(maretime, true));
1998 			}
1999 #undef VFLAGS
2000 		}
2001 	}
2002 
2003 	// Ideya time remaining
2004 	if (!stplyr->exiting && stplyr->nightstime > 0 && LUA_HudEnabled(hud_nightstime))
2005 	{
2006 		INT32 realnightstime = stplyr->nightstime/TICRATE;
2007 		INT32 numbersize;
2008 		UINT8 col = ((realnightstime < 10) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4);
2009 
2010 		if (G_IsSpecialStage(gamemap))
2011 		{
2012 			tic_t lowest_time = stplyr->nightstime;
2013 			INT32 i;
2014 			for (i = 0; i < MAXPLAYERS; i++)
2015 				if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE && players[i].nightstime < lowest_time)
2016 					lowest_time = players[i].nightstime;
2017 			realnightstime = lowest_time/TICRATE;
2018 		}
2019 
2020 		if (stplyr->powers[pw_flashing] > TICRATE) // was hit
2021 		{
2022 			UINT16 flashingLeft = stplyr->powers[pw_flashing]-(TICRATE);
2023 			if (flashingLeft < TICRATE/2) // Start fading out
2024 			{
2025 				UINT32 fadingFlag = (9 - 9*flashingLeft/(TICRATE/2)) << V_ALPHASHIFT;
2026 				V_DrawTranslucentPatch(160 - (minus5sec->width/2), 28, V_PERPLAYER|fadingFlag, minus5sec);
2027 			}
2028 			else
2029 				V_DrawScaledPatch(160 - (minus5sec->width/2), 28, V_PERPLAYER, minus5sec);
2030 		}
2031 
2032 		if (realnightstime < 10)
2033 			numbersize = 16/2;
2034 		else if (realnightstime < 100)
2035 			numbersize = 32/2;
2036 		else
2037 			numbersize = 48/2;
2038 
2039 		if ((oldspecialstage && leveltime & 2)
2040 			&& (stplyr->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
2041 			&& !(stplyr->powers[pw_shield] & SH_PROTECTWATER))
2042 			col = SKINCOLOR_ORANGE;
2043 
2044 		ST_DrawNightsOverlayNum((160 + numbersize)<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP, realnightstime, nightsnum, col);
2045 
2046 		// Show exact time in debug
2047 		if (cv_debug & DBG_NIGHTSBASIC)
2048 			V_DrawString(160 + numbersize + 8, 24, V_SNAPTOTOP|((realnightstime < 10) ? V_REDMAP : V_YELLOWMAP), va("%02d", G_TicsToCentiseconds(stplyr->nightstime)));
2049 	}
2050 
2051 	if (oldspecialstage)
2052 	{
2053 		if (leveltime < 5*TICRATE)
2054 		{
2055 			INT32 aflag = V_PERPLAYER;
2056 			tic_t drawtime = (5*TICRATE) - leveltime;
2057 			if (drawtime < TICRATE/2)
2058 				aflag |= (9 - 9*drawtime/(TICRATE/2)) << V_ALPHASHIFT;
2059 			// This one, not quite as much so.
2060 			V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
2061 		                     va(M_GetText("\x80GET\x82 %d\x80 SPHERE%s!"), ssspheres,
2062 		                        (ssspheres > 1) ? "S" : ""));
2063 		}
2064 	}
2065 	else
2066 	{
2067 		// Show pickup durations
2068 		if (cv_debug & DBG_NIGHTSBASIC)
2069 		{
2070 			UINT16 pwr;
2071 
2072 			if (stplyr->powers[pw_nights_superloop])
2073 			{
2074 				pwr = stplyr->powers[pw_nights_superloop];
2075 				V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_SPRITE));
2076 				V_DrawThinString(106, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
2077 			}
2078 
2079 			if (stplyr->powers[pw_nights_helper])
2080 			{
2081 				pwr = stplyr->powers[pw_nights_helper];
2082 				V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_SPRITE));
2083 				V_DrawThinString(146, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
2084 			}
2085 
2086 			if (stplyr->powers[pw_nights_linkfreeze])
2087 			{
2088 				pwr = stplyr->powers[pw_nights_linkfreeze];
2089 				V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_SPRITE));
2090 				V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
2091 			}
2092 		}
2093 
2094 		// Records/extra text
2095 		if (LUA_HudEnabled(hud_nightsrecords))
2096 			ST_drawNightsRecords();
2097 	}
2098 }
2099 
ST_drawWeaponSelect(INT32 xoffs,INT32 y)2100 static void ST_drawWeaponSelect(INT32 xoffs, INT32 y)
2101 {
2102 	INT32 q = stplyr->weapondelay, del = 0, p = 16;
2103 	while (q)
2104 	{
2105 		if (q > p)
2106 		{
2107 			del += p;
2108 			q -= p;
2109 			q /= 2;
2110 			if (p > 1)
2111 				p /= 2;
2112 		}
2113 		else
2114 		{
2115 			del += q;
2116 			break;
2117 		}
2118 	}
2119 	V_DrawScaledPatch(6 + xoffs, y-2 - del/2, V_PERPLAYER|V_SNAPTOBOTTOM, curweapon);
2120 }
2121 
ST_drawWeaponRing(powertype_t weapon,INT32 rwflag,INT32 wepflag,INT32 xoffs,INT32 y,patch_t * pat)2122 static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, INT32 xoffs, INT32 y, patch_t *pat)
2123 {
2124 	INT32 txtflags = 0, patflags = 0;
2125 
2126 	if (stplyr->powers[weapon])
2127 	{
2128 		if (stplyr->powers[weapon] >= rw_maximums[wepflag])
2129 			txtflags |= V_YELLOWMAP;
2130 
2131 		if (weapon == pw_infinityring
2132 		|| (stplyr->ringweapons & rwflag))
2133 			; //txtflags |= V_20TRANS;
2134 		else
2135 		{
2136 			txtflags |= V_TRANSLUCENT;
2137 			patflags =  V_80TRANS;
2138 		}
2139 
2140 		V_DrawScaledPatch(8 + xoffs, y, V_PERPLAYER|V_SNAPTOBOTTOM|patflags, pat);
2141 		V_DrawRightAlignedThinString(24 + xoffs, y + 8, V_PERPLAYER|V_SNAPTOBOTTOM|txtflags, va("%d", stplyr->powers[weapon]));
2142 
2143 		if (stplyr->currentweapon == wepflag)
2144 			ST_drawWeaponSelect(xoffs, y);
2145 	}
2146 	else if (stplyr->ringweapons & rwflag)
2147 		V_DrawScaledPatch(8 + xoffs, y, V_PERPLAYER|V_SNAPTOBOTTOM|V_TRANSLUCENT, pat);
2148 }
2149 
ST_drawMatchHUD(void)2150 static void ST_drawMatchHUD(void)
2151 {
2152 	char penaltystr[7];
2153 	const INT32 y = 176; // HUD_LIVES
2154 	INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6;
2155 
2156 	if (F_GetPromptHideHud(y))
2157 		return;
2158 
2159 	if (!G_RingSlingerGametype())
2160 		return;
2161 
2162 	if (G_TagGametype() && !(stplyr->pflags & PF_TAGIT))
2163 		return;
2164 
2165 	{
2166 		if (stplyr->powers[pw_infinityring])
2167 			ST_drawWeaponRing(pw_infinityring, 0, 0, offset, y, infinityring);
2168 		else
2169 		{
2170 			if (stplyr->rings > 0)
2171 				V_DrawScaledPatch(8 + offset, y, V_PERPLAYER|V_SNAPTOBOTTOM, normring);
2172 			else
2173 				V_DrawTranslucentPatch(8 + offset, y, V_PERPLAYER|V_SNAPTOBOTTOM|V_80TRANS, normring);
2174 
2175 			if (!stplyr->currentweapon)
2176 				ST_drawWeaponSelect(offset, y);
2177 		}
2178 
2179 		ST_drawWeaponRing(pw_automaticring, RW_AUTO, WEP_AUTO, offset + 20, y, autoring);
2180 		ST_drawWeaponRing(pw_bouncering, RW_BOUNCE, WEP_BOUNCE, offset + 40, y, bouncering);
2181 		ST_drawWeaponRing(pw_scatterring, RW_SCATTER, WEP_SCATTER, offset + 60, y, scatterring);
2182 		ST_drawWeaponRing(pw_grenadering, RW_GRENADE, WEP_GRENADE, offset + 80, y, grenadering);
2183 		ST_drawWeaponRing(pw_explosionring, RW_EXPLODE, WEP_EXPLODE, offset + 100, y, explosionring);
2184 		ST_drawWeaponRing(pw_railring, RW_RAIL, WEP_RAIL, offset + 120, y, railring);
2185 
2186 		if (stplyr->ammoremovaltimer && leveltime % 8 < 4)
2187 		{
2188 			sprintf(penaltystr, "-%d", stplyr->ammoremoval);
2189 			V_DrawString(offset + 8 + stplyr->ammoremovalweapon * 20, y,
2190 				V_REDMAP|V_SNAPTOBOTTOM, penaltystr);
2191 		}
2192 
2193 	}
2194 }
2195 
ST_drawTextHUD(void)2196 static void ST_drawTextHUD(void)
2197 {
2198 	INT32 y = 42 + 16; // HUD_RINGS
2199 	boolean donef12 = false;
2200 
2201 #define textHUDdraw(str) \
2202 {\
2203 	V_DrawThinString(16, y, V_PERPLAYER|V_HUDTRANS|V_SNAPTOLEFT|V_SNAPTOTOP, str);\
2204 	y += 8;\
2205 }
2206 
2207 	if (F_GetPromptHideHud(y))
2208 		return;
2209 
2210 	if (stplyr->spectator && (!G_CoopGametype() || stplyr->playerstate == PST_LIVE))
2211 		textHUDdraw(M_GetText("\x86""Spectator mode:"))
2212 
2213 	if (circuitmap)
2214 	{
2215 		if (stplyr->exiting)
2216 			textHUDdraw(M_GetText("\x82""FINISHED!"))
2217 		else
2218 			textHUDdraw(va("Lap:""\x82 %u/%d", stplyr->laps+1, cv_numlaps.value))
2219 	}
2220 
2221 	if (!G_CoopGametype() && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)))
2222 	{
2223 		if (!splitscreen && !donef12)
2224 		{
2225 			textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
2226 			donef12 = true;
2227 		}
2228 	}
2229 	else if ((gametyperules & GTR_RESPAWNDELAY) && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
2230 	{
2231 		INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE;
2232 
2233 		if (respawntime > 0 && !stplyr->spectator)
2234 			textHUDdraw(va(M_GetText("Respawn in %d..."), respawntime))
2235 		else
2236 			textHUDdraw(M_GetText("\x82""JUMP:""\x80 Respawn"))
2237 	}
2238 	else if (stplyr->spectator && (!G_CoopGametype() || stplyr->playerstate == PST_LIVE))
2239 	{
2240 		if (!splitscreen && !donef12)
2241 		{
2242 			textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
2243 			donef12 = true;
2244 		}
2245 
2246 		textHUDdraw(M_GetText("\x82""JUMP:""\x80 Rise"))
2247 		textHUDdraw(M_GetText("\x82""SPIN:""\x80 Lower"))
2248 
2249 		if (G_IsSpecialStage(gamemap))
2250 			textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
2251 		else if (G_PlatformGametype())
2252 		{
2253 			if (G_GametypeUsesCoopLives())
2254 			{
2255 				if (stplyr->lives <= 0
2256 				&& cv_cooplives.value == 2
2257 				&& (netgame || multiplayer))
2258 				{
2259 					INT32 i;
2260 					for (i = 0; i < MAXPLAYERS; i++)
2261 					{
2262 						if (!playeringame[i])
2263 							continue;
2264 
2265 						if (&players[i] == stplyr)
2266 							continue;
2267 
2268 						if (players[i].lives > 1)
2269 							break;
2270 						}
2271 
2272 					if (i != MAXPLAYERS)
2273 						textHUDdraw(M_GetText("You'll steal a life on respawn..."))
2274 					else
2275 						textHUDdraw(M_GetText("Wait to respawn..."))
2276 				}
2277 				else
2278 					textHUDdraw(M_GetText("Wait to respawn..."))
2279 			}
2280 		}
2281 		else if (G_GametypeHasSpectators())
2282 			textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
2283 	}
2284 
2285 	if (G_CoopGametype() && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED)))
2286 	{
2287 		UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
2288 		if (numneeded)
2289 		{
2290 			INT32 i, total = 0, exiting = 0;
2291 
2292 			for (i = 0; i < MAXPLAYERS; i++)
2293 			{
2294 				if (!playeringame[i] || players[i].spectator)
2295 					continue;
2296 				if (players[i].lives <= 0)
2297 					continue;
2298 
2299 				total++;
2300 				if (players[i].exiting || (players[i].pflags & PF_FINISHED))
2301 					exiting++;
2302 			}
2303 
2304 			if (numneeded != 4)
2305 			{
2306 				total *= cv_playersforexit.value;
2307 				if (total & 3)
2308 					total += 4; // round up
2309 				total /= 4;
2310 			}
2311 
2312 			if (exiting < total)
2313 			{
2314 				if (!splitscreen && !donef12)
2315 				{
2316 					textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
2317 					donef12 = true;
2318 				}
2319 				total -= exiting;
2320 				textHUDdraw(va(M_GetText("%d player%s remaining"), total, ((total == 1) ? "" : "s")))
2321 			}
2322 		}
2323 	}
2324 	else if ((gametyperules & GTR_TAG) && (!stplyr->spectator))
2325 	{
2326 		if (leveltime < hidetime * TICRATE)
2327 		{
2328 			if (stplyr->pflags & PF_TAGIT)
2329 			{
2330 				if (gametyperules & GTR_BLINDFOLDED)
2331 					textHUDdraw(M_GetText("\x82""You are blindfolded!"))
2332 				textHUDdraw(M_GetText("Waiting for players to hide..."))
2333 			}
2334 			else if (gametyperules & GTR_HIDEFROZEN)
2335 				textHUDdraw(M_GetText("Hide before time runs out!"))
2336 			else
2337 				textHUDdraw(M_GetText("Flee before you are hunted!"))
2338 		}
2339 		else if ((gametyperules & GTR_HIDEFROZEN) && !(stplyr->pflags & PF_TAGIT))
2340 		{
2341 			if (!splitscreen && !donef12)
2342 			{
2343 				textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
2344 				donef12 = true;
2345 			}
2346 			textHUDdraw(M_GetText("You cannot move while hiding."))
2347 		}
2348 	}
2349 
2350 #undef textHUDdraw
2351 
2352 }
2353 
ST_drawRaceHUD(void)2354 static inline void ST_drawRaceHUD(void)
2355 {
2356 	if (leveltime > TICRATE && leveltime <= 5*TICRATE)
2357 		ST_drawRaceNum(4*TICRATE - leveltime);
2358 }
2359 
ST_drawTeamHUD(void)2360 static void ST_drawTeamHUD(void)
2361 {
2362 #define SEP 20
2363 
2364 	if (F_GetPromptHideHud(0)) // y base is 0
2365 		return;
2366 
2367 	rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX);
2368 	bflagico = W_CachePatchName("BFLAGICO", PU_HUDGFX);
2369 	rmatcico = W_CachePatchName("RMATCICO", PU_HUDGFX);
2370 	bmatcico = W_CachePatchName("BMATCICO", PU_HUDGFX);
2371 
2372 	if (LUA_HudEnabled(hud_teamscores))
2373 	{
2374 		if (gametyperules & GTR_TEAMFLAGS)
2375 		{
2376 			V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - (bflagico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, bflagico);
2377 			V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - (rflagico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, rflagico);
2378 		}
2379 		else
2380 		{
2381 			V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - (bmatcico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, bmatcico);
2382 			V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - (rmatcico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, rmatcico);
2383 		}
2384 	}
2385 
2386 	if (!(gametyperules & GTR_TEAMFLAGS))
2387 		goto num;
2388 	{
2389 		INT32 i;
2390 		UINT16 whichflag = 0;
2391 
2392 		// Show which flags aren't at base.
2393 		for (i = 0; i < MAXPLAYERS; i++)
2394 		{
2395 			// Blue flag isn't at base
2396 			if (players[i].gotflag & GF_BLUEFLAG && LUA_HudEnabled(hud_teamscores))
2397 				V_DrawScaledPatch(BASEVIDWIDTH/2 - SEP - (nonicon->width / 2), 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon);
2398 
2399 			// Red flag isn't at base
2400 			if (players[i].gotflag & GF_REDFLAG && LUA_HudEnabled(hud_teamscores))
2401 				V_DrawScaledPatch(BASEVIDWIDTH/2 + SEP - (nonicon2->width / 2), 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon2);
2402 
2403 			whichflag |= players[i].gotflag;
2404 
2405 			if ((whichflag & (GF_REDFLAG|GF_BLUEFLAG)) == (GF_REDFLAG|GF_BLUEFLAG))
2406 				break; // both flags were found, let's stop early
2407 		}
2408 
2409 		// Display a countdown timer showing how much time left until the flag returns to base.
2410 		{
2411 			if (blueflag && blueflag->fuse > 1 && LUA_HudEnabled(hud_teamscores))
2412 				V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (blueflag->fuse / TICRATE)));
2413 
2414 			if (redflag && redflag->fuse > 1 && LUA_HudEnabled(hud_teamscores))
2415 				V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (redflag->fuse / TICRATE)));
2416 		}
2417 	}
2418 
2419 num:
2420 	if (LUA_HudEnabled(hud_teamscores))
2421 		V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", bluescore));
2422 
2423 	if (LUA_HudEnabled(hud_teamscores))
2424 		V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", redscore));
2425 
2426 #undef SEP
2427 }
2428 
2429 /*static void ST_drawSpecialStageHUD(void)
2430 {
2431 	if (ssspheres > 0)
2432 	{
2433 		if (hudinfo[HUD_SS_TOTALRINGS].x)
2434 			ST_DrawNumFromHud(HUD_SS_TOTALRINGS, ssspheres, V_HUDTRANS);
2435 		else if (cv_timetic.value == 2)
2436 			V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
2437 		else
2438 			V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
2439 	}
2440 
2441 	if (leveltime < 5*TICRATE && ssspheres > 0)
2442 	{
2443 		ST_DrawPatchFromHud(HUD_GETRINGS, getall, V_HUDTRANS);
2444 		ST_DrawNumFromHud(HUD_GETRINGSNUM, ssspheres, V_HUDTRANS);
2445 	}
2446 
2447 	if (sstimer)
2448 	{
2449 		V_DrawString(hudinfo[HUD_TIMELEFT].x, hudinfo[HUD_TIMELEFT].y, hudinfo[HUD_TIMELEFT].f|V_PERPLAYER|V_HUDTRANS, M_GetText("TIME LEFT"));
2450 		ST_DrawNumFromHud(HUD_TIMELEFTNUM, sstimer/TICRATE, V_HUDTRANS);
2451 	}
2452 	else
2453 		ST_DrawPatchFromHud(HUD_TIMEUP, timeup, V_HUDTRANS);
2454 }*/
2455 
ST_drawEmeraldHuntIcon(mobj_t * hunt,patch_t ** patches,INT32 offset)2456 static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offset)
2457 {
2458 	INT32 interval, i;
2459 	UINT32 dist = ((UINT32)P_AproxDistance(P_AproxDistance(stplyr->mo->x - hunt->x, stplyr->mo->y - hunt->y), stplyr->mo->z - hunt->z))>>FRACBITS;
2460 
2461 	if (dist < 128)
2462 	{
2463 		i = 5;
2464 		interval = 5;
2465 	}
2466 	else if (dist < 512)
2467 	{
2468 		i = 4;
2469 		interval = 10;
2470 	}
2471 	else if (dist < 1024)
2472 	{
2473 		i = 3;
2474 		interval = 20;
2475 	}
2476 	else if (dist < 2048)
2477 	{
2478 		i = 2;
2479 		interval = 30;
2480 	}
2481 	else if (dist < 3072)
2482 	{
2483 		i = 1;
2484 		interval = 35;
2485 	}
2486 	else
2487 	{
2488 		i = 0;
2489 		interval = 0;
2490 	}
2491 
2492 	if (!F_GetPromptHideHud(hudinfo[HUD_HUNTPICS].y))
2493 		V_DrawScaledPatch(hudinfo[HUD_HUNTPICS].x+offset, hudinfo[HUD_HUNTPICS].y, hudinfo[HUD_HUNTPICS].f|V_PERPLAYER|V_HUDTRANS, patches[i]);
2494 	return interval;
2495 }
2496 
2497 // Separated a few things to stop the SOUND EFFECTS BLARING UGH SHUT UP AAAA
ST_doHuntIconsAndSound(void)2498 static void ST_doHuntIconsAndSound(void)
2499 {
2500 	INT32 interval = 0, newinterval = 0;
2501 
2502 	if (hunt1 && hunt1->health)
2503 		interval = ST_drawEmeraldHuntIcon(hunt1, hunthoming, -20);
2504 
2505 	if (hunt2 && hunt2->health)
2506 	{
2507 		newinterval = ST_drawEmeraldHuntIcon(hunt2, hunthoming, 0);
2508 		if (newinterval && (!interval || newinterval < interval))
2509 			interval = newinterval;
2510 	}
2511 
2512 	if (hunt3 && hunt3->health)
2513 	{
2514 		newinterval = ST_drawEmeraldHuntIcon(hunt3, hunthoming, 20);
2515 		if (newinterval && (!interval || newinterval < interval))
2516 			interval = newinterval;
2517 	}
2518 
2519 	if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0)
2520 		S_StartSound(NULL, sfx_emfind);
2521 }
2522 
ST_doItemFinderIconsAndSound(void)2523 static void ST_doItemFinderIconsAndSound(void)
2524 {
2525 	INT32 emblems[16];
2526 	thinker_t *th;
2527 	mobj_t *mo2;
2528 
2529 	UINT8 stemblems = 0, stunfound = 0;
2530 	INT32 i;
2531 	INT32 interval = 0, newinterval = 0;
2532 	INT32 soffset;
2533 
2534 	for (i = 0; i < numemblems; ++i)
2535 	{
2536 		if (emblemlocations[i].type > ET_SKIN || emblemlocations[i].level != gamemap)
2537 			continue;
2538 
2539 		emblems[stemblems++] = i;
2540 
2541 		if (!emblemlocations[i].collected)
2542 			++stunfound;
2543 
2544 		if (stemblems >= 16)
2545 			break;
2546 	}
2547 	// Found all/none exist? Don't waste our time
2548 	if (!stunfound)
2549 		return;
2550 
2551 	// Scan thinkers to find emblem mobj with these ids
2552 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
2553 	{
2554 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
2555 			continue;
2556 
2557 		mo2 = (mobj_t *)th;
2558 
2559 		if (mo2->type != MT_EMBLEM)
2560 			continue;
2561 
2562 		if (!(mo2->flags & MF_SPECIAL))
2563 			continue;
2564 
2565 		for (i = 0; i < stemblems; ++i)
2566 		{
2567 			if (mo2->health == emblems[i] + 1)
2568 			{
2569 				soffset = (i * 20) - ((stemblems - 1) * 10);
2570 
2571 				newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
2572 				if (newinterval && (!interval || newinterval < interval))
2573 					interval = newinterval;
2574 
2575 				break;
2576 			}
2577 		}
2578 
2579 	}
2580 
2581 	if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0)
2582 		S_StartSound(NULL, sfx_emfind);
2583 }
2584 
2585 //
2586 // Draw the status bar overlay, customisable: the user chooses which
2587 // kind of information to overlay
2588 //
ST_overlayDrawer(void)2589 static void ST_overlayDrawer(void)
2590 {
2591 	// Decide whether to draw the stage title or not
2592 	boolean stagetitle = false;
2593 
2594 	// Check for a valid level title
2595 	// If the HUD is enabled
2596 	// And, if Lua is running, if the HUD library has the stage title enabled
2597 	if (G_IsTitleCardAvailable() && *mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)))
2598 	{
2599 		stagetitle = true;
2600 		ST_preDrawTitleCard();
2601 	}
2602 
2603 	// hu_showscores = auto hide score/time/rings when tab rankings are shown
2604 	if (!(hu_showscores && (netgame || multiplayer)))
2605 	{
2606 		if ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) &&
2607 			!F_GetPromptHideHudAll())
2608 			ST_drawNiGHTSHUD();
2609 		else
2610 		{
2611 			if (LUA_HudEnabled(hud_score))
2612 				ST_drawScore();
2613 			if (LUA_HudEnabled(hud_time))
2614 				ST_drawTime();
2615 			if (LUA_HudEnabled(hud_rings))
2616 				ST_drawRings();
2617 
2618 			if (!modeattacking && LUA_HudEnabled(hud_lives))
2619 				ST_drawLivesArea();
2620 		}
2621 	}
2622 
2623 	// GAME OVER hud
2624 	if (G_GametypeUsesCoopLives()
2625 		&& (netgame || multiplayer)
2626 		&& (cv_cooplives.value == 0))
2627 	;
2628 	else if ((G_GametypeUsesLives() || ((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE)) && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer)))
2629 	{
2630 		INT32 i = MAXPLAYERS;
2631 		INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
2632 
2633 		if (G_GametypeUsesCoopLives()
2634 		&& (netgame || multiplayer)
2635 		&& (cv_cooplives.value != 1))
2636 		{
2637 			for (i = 0; i < MAXPLAYERS; i++)
2638 			{
2639 				if (!playeringame[i])
2640 					continue;
2641 
2642 				if (&players[i] == stplyr)
2643 					continue;
2644 
2645 				if (players[i].lives > 0)
2646 					break;
2647 			}
2648 		}
2649 
2650 		if (i == MAXPLAYERS && deadtimer >= 0)
2651 		{
2652 			INT32 lvlttlx = min(6*deadtimer, BASEVIDWIDTH/2);
2653 			UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS);
2654 
2655 			V_DrawScaledPatch(lvlttlx - 8, BASEVIDHEIGHT/2, flags, (countdown == 1 ? slidtime : slidgame));
2656 			V_DrawScaledPatch(BASEVIDWIDTH + 8 - lvlttlx, BASEVIDHEIGHT/2, flags, slidover);
2657 		}
2658 	}
2659 
2660 	if (G_GametypeHasTeams())
2661 		ST_drawTeamHUD();
2662 
2663 	if (!hu_showscores) // hide the following if TAB is held
2664 	{
2665 		// Countdown timer for Race Mode
2666 		if (countdown > 1)
2667 		{
2668 			tic_t time = countdown/TICRATE + 1;
2669 			if (time < 4)
2670 				ST_drawRaceNum(countdown);
2671 			else
2672 			{
2673 				tic_t num = time;
2674 				INT32 sz = tallnum[0]->width / 2, width = 0;
2675 				do
2676 				{
2677 					width += sz;
2678 					num /= 10;
2679 				} while (num);
2680 				V_DrawTallNum((BASEVIDWIDTH/2) + width, ((3*BASEVIDHEIGHT)>>2) - 7, V_PERPLAYER, time);
2681 				//V_DrawCenteredString(BASEVIDWIDTH/2, 176, V_PERPLAYER, va("%d", countdown/TICRATE + 1));
2682 			}
2683 		}
2684 
2685 		// If you are in overtime, put a big honkin' flashin' message on the screen.
2686 		if (((gametyperules & GTR_TIMELIMIT) && (gametyperules & GTR_OVERTIME)) && cv_overtime.value
2687 		&& (leveltime > (timelimitintics + TICRATE/2)) && cv_timelimit.value && (leveltime/TICRATE % 2 == 0))
2688 			V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_PERPLAYER, M_GetText("OVERTIME!"));
2689 
2690 		// Draw Match-related stuff
2691 		//\note Match HUD is drawn no matter what gametype.
2692 		// ... just not if you're a spectator.
2693 		if (!stplyr->spectator && LUA_HudEnabled(hud_weaponrings))
2694 			ST_drawMatchHUD();
2695 
2696 		// Race HUD Stuff
2697 		if (gametyperules & GTR_RACE)
2698 			ST_drawRaceHUD();
2699 
2700 		// Emerald Hunt Indicators
2701 		if (cv_itemfinder.value && M_SecretUnlocked(SECRET_ITEMFINDER))
2702 			ST_doItemFinderIconsAndSound();
2703 		else
2704 			ST_doHuntIconsAndSound();
2705 
2706 		if(!P_IsLocalPlayer(stplyr))
2707 		{
2708 			char name[MAXPLAYERNAME+1];
2709 			// shorten the name if its more than twelve characters.
2710 			strlcpy(name, player_names[stplyr-players], 13);
2711 
2712 			// Show name of player being displayed
2713 			V_DrawCenteredString((BASEVIDWIDTH/6), BASEVIDHEIGHT-80, 0, M_GetText("Viewpoint:"));
2714 			V_DrawCenteredString((BASEVIDWIDTH/6), BASEVIDHEIGHT-64, V_ALLOWLOWERCASE, name);
2715 		}
2716 
2717 		// This is where we draw all the fun cheese if you have the chasecam off!
2718 		if (!(maptol & TOL_NIGHTS))
2719 		{
2720 			if ((stplyr == &players[displayplayer] && !camera.chase)
2721 			|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
2722 			{
2723 				ST_drawFirstPersonHUD();
2724 				if (cv_powerupdisplay.value)
2725 					ST_drawPowerupHUD();  // same as it ever was...
2726 			}
2727 			else if (cv_powerupdisplay.value == 2)
2728 				ST_drawPowerupHUD();  // same as it ever was...
2729 		}
2730 	}
2731 	else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2)
2732 		ST_drawPowerupHUD(); // same as it ever was...
2733 
2734 	if (!(netgame || multiplayer) || !hu_showscores)
2735 		LUAh_GameHUD(stplyr);
2736 
2737 	// draw level title Tails
2738 	if (stagetitle && (!WipeInAction) && (!WipeStageTitle))
2739 		ST_drawTitleCard();
2740 
2741 	if (!hu_showscores && (netgame || multiplayer) && LUA_HudEnabled(hud_textspectator))
2742 		ST_drawTextHUD();
2743 
2744 	if (modeattacking && !(demoplayback && hu_showscores))
2745 		ST_drawInput();
2746 
2747 	ST_drawDebugInfo();
2748 }
2749 
ST_Drawer(void)2750 void ST_Drawer(void)
2751 {
2752 	if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
2753 	{
2754 		INT32 c = 0;
2755 		switch (cv_seenames.value)
2756 		{
2757 			case 1: // Colorless
2758 				break;
2759 			case 2: // Team
2760 				if (G_GametypeHasTeams())
2761 					c = (seenplayer->ctfteam == 1) ? V_REDMAP : V_BLUEMAP;
2762 				break;
2763 			case 3: // Ally/Foe
2764 			default:
2765 				// Green = Ally, Red = Foe
2766 				if (G_GametypeHasTeams())
2767 					c = (players[consoleplayer].ctfteam == seenplayer->ctfteam) ? V_GREENMAP : V_REDMAP;
2768 				else // Everyone is an ally, or everyone is a foe!
2769 					c = (G_RingSlingerGametype()) ? V_REDMAP : V_GREENMAP;
2770 				break;
2771 		}
2772 
2773 		V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF|c, player_names[seenplayer-players]);
2774 	}
2775 
2776 	// Doom's status bar only updated if necessary.
2777 	// However, ours updates every frame regardless, so the "refresh" param was removed
2778 	//(void)refresh;
2779 
2780 	// force a set of the palette by using doPaletteStuff()
2781 	if (vid.recalc)
2782 		st_palette = -1;
2783 
2784 	// Do red-/gold-shifts from damage/items
2785 #ifdef HWRENDER
2786 	//25/08/99: Hurdler: palette changes is done for all players,
2787 	//                   not only player1! That's why this part
2788 	//                   of code is moved somewhere else.
2789 	if (rendermode == render_soft)
2790 #endif
2791 		if (rendermode != render_none) ST_doPaletteStuff();
2792 
2793 	// Blindfold!
2794 	if ((gametyperules & GTR_BLINDFOLDED)
2795 	&& (leveltime < hidetime * TICRATE))
2796 	{
2797 		if (players[displayplayer].pflags & PF_TAGIT)
2798 		{
2799 			stplyr = &players[displayplayer];
2800 			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31|V_PERPLAYER);
2801 		}
2802 		else if (splitscreen && players[secondarydisplayplayer].pflags & PF_TAGIT)
2803 		{
2804 			stplyr = &players[secondarydisplayplayer];
2805 			V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31|V_PERPLAYER);
2806 		}
2807 	}
2808 
2809 	st_translucency = cv_translucenthud.value;
2810 
2811 	if (st_overlay)
2812 	{
2813 		// No deadview!
2814 		stplyr = &players[displayplayer];
2815 		ST_overlayDrawer();
2816 
2817 		if (splitscreen)
2818 		{
2819 			stplyr = &players[secondarydisplayplayer];
2820 			ST_overlayDrawer();
2821 		}
2822 	}
2823 }
2824