1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id:$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // $Log:$
18 //
19 // DESCRIPTION:
20 //		Intermission screens.
21 //
22 //-----------------------------------------------------------------------------
23 
24 // Enhancements by Graf Zahl
25 
26 #include <ctype.h>
27 #include <stdio.h>
28 
29 #include "m_random.h"
30 #include "m_swap.h"
31 #include "i_system.h"
32 #include "w_wad.h"
33 #include "g_game.h"
34 #include "g_level.h"
35 #include "s_sound.h"
36 #include "doomstat.h"
37 #include "v_video.h"
38 #include "i_video.h"
39 #include "wi_stuff.h"
40 #include "c_console.h"
41 #include "hu_stuff.h"
42 #include "v_palette.h"
43 #include "s_sndseq.h"
44 #include "sc_man.h"
45 #include "v_text.h"
46 #include "gi.h"
47 #include "d_player.h"
48 #include "d_netinf.h"
49 #include "b_bot.h"
50 #include "textures/textures.h"
51 #include "r_data/r_translate.h"
52 #include "templates.h"
53 #include "gstrings.h"
54 
55 // States for the intermission
56 typedef enum
57 {
58 	NoState = -1,
59 	StatCount,
60 	ShowNextLoc,
61 	LeavingIntermission
62 } stateenum_t;
63 
64 CVAR (Bool, wi_percents, true, CVAR_ARCHIVE)
65 CVAR (Bool, wi_showtotaltime, true, CVAR_ARCHIVE)
66 CVAR (Bool, wi_noautostartmap, false, CVAR_USERINFO|CVAR_ARCHIVE)
67 CVAR (Int, wi_autoadvance, 0, CVAR_SERVERINFO)
68 
69 
70 void WI_loadData ();
71 void WI_unloadData ();
72 
73 // GLOBAL LOCATIONS
74 #define WI_TITLEY				2
75 #define WI_SPACINGY 			33
76 
77 // SINGPLE-PLAYER STUFF
78 #define SP_STATSX				50
79 #define SP_STATSY				50
80 
81 #define SP_TIMEX				8
82 #define SP_TIMEY				(200-32)
83 
84 
85 // NET GAME STUFF
86 #define NG_STATSY				50
87 #define NG_STATSX				(32 + star->GetScaledWidth()/2 + 32*!dofrags)
88 
89 #define NG_SPACINGX 			64
90 
91 
92 // DEATHMATCH STUFF
93 #define DM_MATRIXX				42
94 #define DM_MATRIXY				68
95 
96 #define DM_SPACINGX 			40
97 
98 #define DM_TOTALSX				269
99 
100 #define DM_KILLERSX 			10
101 #define DM_KILLERSY 			100
102 #define DM_VICTIMSX 			5
103 #define DM_VICTIMSY 			50
104 
105 // These animation variables, structures, etc. are used for the
106 // DOOM/Ultimate DOOM intermission screen animations.  This is
107 // totally different from any sprite or texture/flat animations
108 typedef enum
109 {
110 	ANIM_ALWAYS,	// determined by patch entry
111 	ANIM_PIC,		// continuous
112 
113 	// condition bitflags
114 	ANIM_IFVISITED=8,
115 	ANIM_IFNOTVISITED=16,
116 	ANIM_IFENTERING=32,
117 	ANIM_IFNOTENTERING=64,
118 	ANIM_IFLEAVING=128,
119 	ANIM_IFNOTLEAVING=256,
120 	ANIM_IFTRAVELLING=512,
121 	ANIM_IFNOTTRAVELLING=1024,
122 
123 	ANIM_TYPE=7,
124 	ANIM_CONDITION=~7,
125 
126 } animenum_t;
127 
128 struct yahpt_t
129 {
130 	int x, y;
131 };
132 
133 struct lnode_t
134 {
135 	int   x;       // x/y coordinate pair structure
136 	int   y;
137 	char level[9];
138 } ;
139 
140 
141 #define FACEBACKOFS 4
142 
143 
144 //
145 // Animation.
146 // There is another anim_t used in p_spec.
147 // (which is why I have renamed this one!)
148 //
149 
150 #define MAX_ANIMATION_FRAMES 20
151 struct in_anim_t
152 {
153 	int			type;	// Made an int so I can use '|'
154 	int 		period;	// period in tics between animations
155 	int 		nanims;	// number of animation frames
156 	yahpt_t 	loc;	// location of animation
157 	int 		data;	// ALWAYS: n/a, RANDOM: period deviation (<256)
158 	FTexture *	p[MAX_ANIMATION_FRAMES];	// actual graphics for frames of animations
159 
160 	// following must be initialized to zero before use!
161 	int 		nexttic;	// next value of bcnt (used in conjunction with period)
162 	int 		ctr;		// next frame number to animate
163 	int 		state;		// used by RANDOM and LEVEL when animating
164 
165 	char		levelname[9];
166 	char		levelname2[9];
167 };
168 
169 static TArray<lnode_t> lnodes;
170 static TArray<in_anim_t> anims;
171 
172 
173 //
174 // GENERAL DATA
175 //
176 
177 //
178 // Locally used stuff.
179 //
180 
181 
182 // States for single-player
183 #define SP_KILLS				0
184 #define SP_ITEMS				2
185 #define SP_SECRET				4
186 #define SP_FRAGS				6
187 #define SP_TIME 				8
188 #define SP_PAR					ST_TIME
189 
190 #define SP_PAUSE				1
191 
192 #define SHOWNEXTLOCDELAY		4			// in seconds
193 
194 static int				acceleratestage;	// used to accelerate or skip a stage
195 static bool				playerready[MAXPLAYERS];
196 static int				me;					// wbs->pnum
197 static stateenum_t		state;				// specifies current state
198 static wbstartstruct_t *wbs;				// contains information passed into intermission
199 static wbplayerstruct_t*plrs;				// wbs->plyr[]
200 static int				cnt;				// used for general timing
201 static int				bcnt;				// used for timing of background animation
202 static int				cnt_kills[MAXPLAYERS];
203 static int				cnt_items[MAXPLAYERS];
204 static int				cnt_secret[MAXPLAYERS];
205 static int				cnt_frags[MAXPLAYERS];
206 static int				cnt_deaths[MAXPLAYERS];
207 static int				cnt_time;
208 static int				cnt_total_time;
209 static int				cnt_par;
210 static int				cnt_pause;
211 static int				total_frags;
212 static int				total_deaths;
213 static bool				noautostartmap;
214 static int				dofrags;
215 static int				ng_state;
216 
217 //
218 //		GRAPHICS
219 //
220 
221 struct FPatchInfo
222 {
223 	FFont *mFont;
224 	FTexture *mPatch;
225 	EColorRange mColor;
226 
InitFPatchInfo227 	void Init(FGIFont &gifont)
228 	{
229 		if (gifont.color == NAME_Null)
230 		{
231 			mPatch = TexMan[gifont.fontname];	// "entering"
232 			mColor = mPatch == NULL? CR_UNTRANSLATED : CR_UNDEFINED;
233 			mFont = NULL;
234 		}
235 		else
236 		{
237 			mFont = V_GetFont(gifont.fontname);
238 			mColor = V_FindFontColor(gifont.color);
239 			mPatch = NULL;
240 		}
241 		if (mFont == NULL)
242 		{
243 			mFont = BigFont;
244 		}
245 	}
246 };
247 
248 static FPatchInfo mapname;
249 static FPatchInfo finished;
250 static FPatchInfo entering;
251 
252 static TArray<FTexture *> yah; 		// You Are Here graphic
253 static FTexture* 		splat;		// splat
254 static FTexture* 		sp_secret;	// "secret"
255 static FTexture* 		kills;		// "Kills", "Scrt", "Items", "Frags"
256 static FTexture* 		secret;
257 static FTexture* 		items;
258 static FTexture* 		frags;
259 static FTexture* 		timepic;	// Time sucks.
260 static FTexture* 		par;
261 static FTexture* 		sucks;
262 static FTexture* 		killers;	// "killers", "victims"
263 static FTexture* 		victims;
264 static FTexture* 		total;		// "Total", your face, your dead face
265 //static FTexture* 		star;
266 //static FTexture* 		bstar;
267 static FTexture* 		p;			// Player graphic
268 static FTexture*		lnames[2];	// Name graphics of each level (centered)
269 
270 // [RH] Info to dynamically generate the level name graphics
271 static FString			lnametexts[2];
272 
273 static FTexture			*background;
274 
275 //
276 // CODE
277 //
278 
279 // ====================================================================
280 //
281 // Background script commands
282 //
283 // ====================================================================
284 
285 static const char *WI_Cmd[]={
286 	"Background",
287 	"Splat",
288 	"Pointer",
289 	"Spots",
290 
291 	"IfEntering",
292 	"IfNotEntering",
293 	"IfVisited",
294 	"IfNotVisited",
295 	"IfLeaving",
296 	"IfNotLeaving",
297 	"IfTravelling",
298 	"IfNotTravelling",
299 
300 	"Animation",
301 	"Pic",
302 
303 	"NoAutostartMap",
304 
305 	NULL
306 };
307 
308 //====================================================================
309 //
310 //	Loads the background - either from a single texture
311 //	or an intermission lump.
312 //	Unfortunately the texture manager is incapable of recognizing text
313 //	files so if you use a script you have to prefix its name by '$' in
314 //  MAPINFO.
315 //
316 //====================================================================
IsExMy(const char * name)317 static bool IsExMy(const char * name)
318 {
319 	// Only check for the first 3 episodes. They are the only ones with default intermission scripts.
320 	// Level names can be upper- and lower case so use tolower to check!
321 	return (tolower(name[0])=='e' && name[1]>='1' && name[1]<='3' && tolower(name[2])=='m');
322 }
323 
WI_LoadBackground(bool isenterpic)324 void WI_LoadBackground(bool isenterpic)
325 {
326 	const char *lumpname = NULL;
327 	char buffer[10];
328 	in_anim_t an;
329 	lnode_t pt;
330 	FTextureID texture;
331 
332 	bcnt=0;
333 
334 	texture.SetInvalid();
335 	if (isenterpic)
336 	{
337 		level_info_t * li = FindLevelInfo(wbs->next);
338 		if (li != NULL) lumpname = li->EnterPic;
339 	}
340 	else
341 	{
342 		lumpname = level.info->ExitPic;
343 	}
344 
345 	// Try to get a default if nothing specified
346 	if (lumpname == NULL || lumpname[0]==0)
347 	{
348 		lumpname = NULL;
349 		switch(gameinfo.gametype)
350 		{
351 		case GAME_Chex:
352 		case GAME_Doom:
353 			if (!(gameinfo.flags & GI_MAPxx))
354 			{
355 				const char *level = isenterpic ? wbs->next : wbs->current;
356 				if (IsExMy(level))
357 				{
358 					mysnprintf(buffer, countof(buffer), "$IN_EPI%c", level[1]);
359 					lumpname = buffer;
360 				}
361 			}
362 			if (!lumpname)
363 			{
364 				if (isenterpic)
365 				{
366 					// One special case needs to be handled here!
367 					// If going from E1-E3 to E4 the default should be used, not the exit pic.
368 
369 					// Not if the exit pic is user defined!
370 					if (level.info->ExitPic.IsNotEmpty()) return;
371 
372 					// E1-E3 need special treatment when playing Doom 1.
373 					if (!(gameinfo.flags & GI_MAPxx))
374 					{
375 						// not if the last level is not from the first 3 episodes
376 						if (!IsExMy(wbs->current)) return;
377 
378 						// not if the next level is one of the first 3 episodes
379 						if (IsExMy(wbs->next)) return;
380 					}
381 				}
382 				lumpname = "INTERPIC";
383 			}
384 			break;
385 
386 		case GAME_Heretic:
387 			if (isenterpic)
388 			{
389 				if (IsExMy(wbs->next))
390 				{
391 					mysnprintf(buffer, countof(buffer), "$IN_HTC%c", wbs->next[1]);
392 					lumpname = buffer;
393 				}
394 			}
395 			if (!lumpname)
396 			{
397 				if (isenterpic) return;
398 				lumpname = "FLOOR16";
399 			}
400 			break;
401 
402 		case GAME_Hexen:
403 			if (isenterpic) return;
404 			lumpname = "INTERPIC";
405 			break;
406 
407 		case GAME_Strife:
408 		default:
409 			// Strife doesn't have an intermission pic so choose something neutral.
410 			if (isenterpic) return;
411 			lumpname = gameinfo.BorderFlat;
412 			break;
413 		}
414 	}
415 	if (lumpname == NULL)
416 	{
417 		// shouldn't happen!
418 		background = NULL;
419 		return;
420 	}
421 
422 	lnodes.Clear();
423 	anims.Clear();
424 	yah.Clear();
425 	splat = NULL;
426 
427 	// a name with a starting '$' indicates an intermission script
428 	if (*lumpname!='$')
429 	{
430 		texture = TexMan.CheckForTexture(lumpname, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny);
431 	}
432 	else
433 	{
434 		int lumpnum=Wads.CheckNumForFullName(lumpname+1, true);
435 		if (lumpnum>=0)
436 		{
437 			FScanner sc(lumpnum);
438 			while (sc.GetString())
439 			{
440 				memset(&an,0,sizeof(an));
441 				int caseval = sc.MustMatchString(WI_Cmd);
442 				switch(caseval)
443 				{
444 				case 0:		// Background
445 					sc.MustGetString();
446 					texture = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny);
447 					break;
448 
449 				case 1:		// Splat
450 					sc.MustGetString();
451 					splat = TexMan[sc.String];
452 					break;
453 
454 				case 2:		// Pointers
455 					while (sc.GetString() && !sc.Crossed)
456 					{
457 						yah.Push(TexMan[sc.String]);
458 					}
459 					if (sc.Crossed)
460 						sc.UnGet();
461 					break;
462 
463 				case 3:		// Spots
464 					sc.MustGetStringName("{");
465 					while (!sc.CheckString("}"))
466 					{
467 						sc.MustGetString();
468 						strncpy(pt.level, sc.String,8);
469 						pt.level[8] = 0;
470 						sc.MustGetNumber();
471 						pt.x = sc.Number;
472 						sc.MustGetNumber();
473 						pt.y = sc.Number;
474 						lnodes.Push(pt);
475 					}
476 					break;
477 
478 				case 4:		// IfEntering
479 					an.type = ANIM_IFENTERING;
480 					goto readanimation;
481 
482 				case 5:		// IfEntering
483 					an.type = ANIM_IFNOTENTERING;
484 					goto readanimation;
485 
486 				case 6:		// IfVisited
487 					an.type = ANIM_IFVISITED;
488 					goto readanimation;
489 
490 				case 7:		// IfNotVisited
491 					an.type = ANIM_IFNOTVISITED;
492 					goto readanimation;
493 
494 				case 8:		// IfLeaving
495 					an.type = ANIM_IFLEAVING;
496 					goto readanimation;
497 
498 				case 9:		// IfNotLeaving
499 					an.type = ANIM_IFNOTLEAVING;
500 					goto readanimation;
501 
502 				case 10:	// IfTravelling
503 					an.type = ANIM_IFTRAVELLING;
504 					sc.MustGetString();
505 					strncpy(an.levelname2, sc.String, 8);
506 					an.levelname2[8] = 0;
507 					goto readanimation;
508 
509 				case 11:	// IfNotTravelling
510 					an.type = ANIM_IFTRAVELLING;
511 					sc.MustGetString();
512 					strncpy(an.levelname2, sc.String, 8);
513 					an.levelname2[8] = 0;
514 					goto readanimation;
515 
516 				case 14:	// NoAutostartMap
517 					noautostartmap = true;
518 					break;
519 
520 				readanimation:
521 					sc.MustGetString();
522 					strncpy(an.levelname, sc.String, 8);
523 					an.levelname[8] = 0;
524 					sc.MustGetString();
525 					caseval=sc.MustMatchString(WI_Cmd);
526 
527 				default:
528 					switch (caseval)
529 					{
530 					case 12:	// Animation
531 						an.type |= ANIM_ALWAYS;
532 						sc.MustGetNumber();
533 						an.loc.x = sc.Number;
534 						sc.MustGetNumber();
535 						an.loc.y = sc.Number;
536 						sc.MustGetNumber();
537 						an.period = sc.Number;
538 						an.nexttic = 1 + (M_Random() % an.period);
539 						if (sc.GetString())
540 						{
541 							if (sc.Compare("ONCE"))
542 							{
543 								an.data = 1;
544 							}
545 							else
546 							{
547 								sc.UnGet();
548 							}
549 						}
550 						if (!sc.CheckString("{"))
551 						{
552 							sc.MustGetString();
553 							an.p[an.nanims++] = TexMan[sc.String];
554 						}
555 						else
556 						{
557 							while (!sc.CheckString("}"))
558 							{
559 								sc.MustGetString();
560 								if (an.nanims<MAX_ANIMATION_FRAMES)
561 									an.p[an.nanims++] = TexMan[sc.String];
562 							}
563 						}
564 						an.ctr = -1;
565 						anims.Push(an);
566 						break;
567 
568 					case 13:		// Pic
569 						an.type |= ANIM_PIC;
570 						sc.MustGetNumber();
571 						an.loc.x = sc.Number;
572 						sc.MustGetNumber();
573 						an.loc.y = sc.Number;
574 						sc.MustGetString();
575 						an.p[0] = TexMan[sc.String];
576 						anims.Push(an);
577 						break;
578 
579 					default:
580 						sc.ScriptError("Unknown token %s in intermission script", sc.String);
581 					}
582 				}
583 			}
584 		}
585 		else
586 		{
587 			Printf("Intermission script %s not found!\n", lumpname+1);
588 			texture = TexMan.GetTexture("INTERPIC", FTexture::TEX_MiscPatch);
589 		}
590 	}
591 	background=TexMan[texture];
592 }
593 
594 //====================================================================
595 //
596 //	made this more generic and configurable through a script
597 //	Removed all the ugly special case handling for different game modes
598 //
599 //====================================================================
600 
WI_updateAnimatedBack()601 void WI_updateAnimatedBack()
602 {
603 	unsigned int i;
604 
605 	for(i=0;i<anims.Size();i++)
606 	{
607 		in_anim_t * a = &anims[i];
608 		switch (a->type & ANIM_TYPE)
609 		{
610 		case ANIM_ALWAYS:
611 			if (bcnt >= a->nexttic)
612 			{
613 				if (++a->ctr >= a->nanims)
614 				{
615 					if (a->data==0) a->ctr = 0;
616 					else a->ctr--;
617 				}
618 				a->nexttic = bcnt + a->period;
619 			}
620 			break;
621 
622 		case ANIM_PIC:
623 			a->ctr = 0;
624 			break;
625 
626 		}
627 	}
628 }
629 
630 //====================================================================
631 //
632 //	Draws the background including all animations
633 //
634 //====================================================================
635 
WI_drawBackground()636 void WI_drawBackground()
637 {
638 	unsigned int i;
639 	double animwidth=320;		// For a flat fill or clear background scale animations to 320x200
640 	double animheight=200;
641 
642 	if (background)
643 	{
644 		// background
645 		if (background->UseType == FTexture::TEX_MiscPatch)
646 		{
647 			// scale all animations below to fit the size of the base pic
648 			// The base pic is always scaled to fit the screen so this allows
649 			// placing the animations precisely where they belong on the base pic
650 			animwidth = background->GetScaledWidthDouble();
651 			animheight = background->GetScaledHeightDouble();
652 			screen->FillBorder (NULL);
653 			screen->DrawTexture(background, 0, 0, DTA_Fullscreen, true, TAG_DONE);
654 		}
655 		else
656 		{
657 			screen->FlatFill(0, 0, SCREENWIDTH, SCREENHEIGHT, background);
658 		}
659 	}
660 	else
661 	{
662 		screen->Clear(0,0, SCREENWIDTH, SCREENHEIGHT, 0, 0);
663 	}
664 
665 	for(i=0;i<anims.Size();i++)
666 	{
667 		in_anim_t * a = &anims[i];
668 		level_info_t * li;
669 
670 		switch (a->type & ANIM_CONDITION)
671 		{
672 		case ANIM_IFVISITED:
673 			li = FindLevelInfo(a->levelname);
674 			if (li == NULL || !(li->flags & LEVEL_VISITED)) continue;
675 			break;
676 
677 		case ANIM_IFNOTVISITED:
678 			li = FindLevelInfo(a->levelname);
679 			if (li == NULL || (li->flags & LEVEL_VISITED)) continue;
680 			break;
681 
682 			// StatCount means 'leaving' - everything else means 'entering'!
683 		case ANIM_IFENTERING:
684 			if (state == StatCount || strnicmp(a->levelname, wbs->next, 8)) continue;
685 			break;
686 
687 		case ANIM_IFNOTENTERING:
688 			if (state != StatCount && !strnicmp(a->levelname, wbs->next, 8)) continue;
689 			break;
690 
691 		case ANIM_IFLEAVING:
692 			if (state != StatCount || strnicmp(a->levelname, wbs->current, 8)) continue;
693 			break;
694 
695 		case ANIM_IFNOTLEAVING:
696 			if (state == StatCount && !strnicmp(a->levelname, wbs->current, 8)) continue;
697 			break;
698 
699 		case ANIM_IFTRAVELLING:
700 			if (strnicmp(a->levelname2, wbs->current, 8) || strnicmp(a->levelname, wbs->next, 8)) continue;
701 			break;
702 
703 		case ANIM_IFNOTTRAVELLING:
704 			if (!strnicmp(a->levelname2, wbs->current, 8) && !strnicmp(a->levelname, wbs->next, 8)) continue;
705 			break;
706 		}
707 		if (a->ctr >= 0)
708 			screen->DrawTexture(a->p[a->ctr], a->loc.x, a->loc.y,
709 								DTA_VirtualWidthF, animwidth, DTA_VirtualHeightF, animheight, TAG_DONE);
710 	}
711 }
712 
713 
714 //====================================================================
715 //
716 // Draws a single character with a shadow
717 //
718 //====================================================================
719 
WI_DrawCharPatch(FFont * font,int charcode,int x,int y,EColorRange translation=CR_UNTRANSLATED,bool nomove=false)720 static int WI_DrawCharPatch (FFont *font, int charcode, int x, int y, EColorRange translation=CR_UNTRANSLATED, bool nomove=false)
721 {
722 	int width;
723 	screen->DrawTexture(font->GetChar(charcode, &width), x, y,
724 		nomove ? DTA_CleanNoMove : DTA_Clean, true,
725 		DTA_ShadowAlpha, (gameinfo.gametype & GAME_DoomChex) ? 0 : FRACUNIT/2,
726 		DTA_Translation, font->GetColorTranslation(translation),
727 		TAG_DONE);
728 	return x - width;
729 }
730 
731 //====================================================================
732 //
733 // CheckRealHeight
734 //
735 // Checks the posts in a texture and returns the lowest row (plus one)
736 // of the texture that is actually used.
737 //
738 //====================================================================
739 
CheckRealHeight(FTexture * tex)740 int CheckRealHeight(FTexture *tex)
741 {
742 	const FTexture::Span *span;
743 	int maxy = 0, miny = tex->GetHeight();
744 
745 	for (int i = 0; i < tex->GetWidth(); ++i)
746 	{
747 		tex->GetColumn(i, &span);
748 		while (span->Length != 0)
749 		{
750 			if (span->TopOffset < miny)
751 			{
752 				miny = span->TopOffset;
753 			}
754 			if (span->TopOffset + span->Length > maxy)
755 			{
756 				maxy = span->TopOffset + span->Length;
757 			}
758 			span++;
759 		}
760 	}
761 	// Scale maxy before returning it
762 	maxy = (maxy << 17) / tex->yScale;
763 	maxy = (maxy >> 1) + (maxy & 1);
764 	return maxy;
765 }
766 
767 //====================================================================
768 //
769 // Draws a level name with the big font
770 //
771 // x is no longer passed as a parameter because the text is now broken into several lines
772 // if it is too long
773 //
774 //====================================================================
775 
WI_DrawName(int y,FTexture * tex,const char * levelname)776 int WI_DrawName(int y, FTexture *tex, const char *levelname)
777 {
778 	// draw <LevelName>
779 	if (tex)
780 	{
781 		screen->DrawTexture(tex, (screen->GetWidth() - tex->GetScaledWidth()*CleanXfac) /2, y, DTA_CleanNoMove, true, TAG_DONE);
782 		int h = tex->GetScaledHeight();
783 		if (h > 50)
784 		{ // Fix for Deus Vult II and similar wads that decide to make these hugely tall
785 		  // patches with vast amounts of empty space at the bottom.
786 			h = CheckRealHeight(tex);
787 		}
788 		return y + (h + BigFont->GetHeight()/4) * CleanYfac;
789 	}
790 	else
791 	{
792 		int i;
793 		size_t l;
794 		const char *p;
795 		int h = 0;
796 		int lumph;
797 
798 		lumph = mapname.mFont->GetHeight() * CleanYfac;
799 
800 		p = levelname;
801 		if (!p) return 0;
802 		l = strlen(p);
803 		if (!l) return 0;
804 
805 		FBrokenLines *lines = V_BreakLines(mapname.mFont, screen->GetWidth() / CleanXfac, p);
806 
807 		if (lines)
808 		{
809 			for (i = 0; lines[i].Width >= 0; i++)
810 			{
811 				screen->DrawText(mapname.mFont, mapname.mColor, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h,
812 					lines[i].Text, DTA_CleanNoMove, true, TAG_DONE);
813 				h += lumph;
814 			}
815 			V_FreeBrokenLines(lines);
816 		}
817 		return y + h + lumph/4;
818 	}
819 }
820 
821 //====================================================================
822 //
823 // Draws a text, either as patch or as string from the string table
824 //
825 //====================================================================
826 
WI_DrawPatchText(int y,FPatchInfo * pinfo,const char * stringname)827 int WI_DrawPatchText(int y, FPatchInfo *pinfo, const char *stringname)
828 {
829 	const char *string = GStrings(stringname);
830 	int midx = screen->GetWidth() / 2;
831 
832 	if (pinfo->mPatch != NULL)
833 	{
834 		screen->DrawTexture(pinfo->mPatch, midx - pinfo->mPatch->GetScaledWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE);
835 		return y + (pinfo->mPatch->GetScaledHeight() * CleanYfac);
836 	}
837 	else
838 	{
839 		screen->DrawText(pinfo->mFont, pinfo->mColor, midx - pinfo->mFont->StringWidth(string)*CleanXfac/2,
840 			y, string, DTA_CleanNoMove, true, TAG_DONE);
841 		return y + pinfo->mFont->GetHeight() * CleanYfac;
842 	}
843 }
844 
845 
846 //====================================================================
847 //
848 // Draws "<Levelname> Finished!"
849 //
850 // Either uses the specified patch or the big font
851 // A level name patch can be specified for all games now, not just Doom.
852 //
853 //====================================================================
854 
WI_drawLF()855 int WI_drawLF ()
856 {
857 	int y = WI_TITLEY * CleanYfac;
858 
859 	y = WI_DrawName(y, wbs->LName0, lnametexts[0]);
860 
861 	// Adjustment for different font sizes for map name and 'finished'.
862 	y -= ((mapname.mFont->GetHeight() - finished.mFont->GetHeight()) * CleanYfac) / 4;
863 
864 	// draw "Finished!"
865 	if (y < (NG_STATSY - finished.mFont->GetHeight()*3/4) * CleanYfac)
866 	{
867 		// don't draw 'finished' if the level name is too tall
868 		y = WI_DrawPatchText(y, &finished, "WI_FINISHED");
869 	}
870 	return y;
871 }
872 
873 
874 //====================================================================
875 //
876 // Draws "Entering <LevelName>"
877 //
878 // Either uses the specified patch or the big font
879 // A level name patch can be specified for all games now, not just Doom.
880 //
881 //====================================================================
882 
WI_drawEL()883 void WI_drawEL ()
884 {
885 	int y = WI_TITLEY * CleanYfac;
886 
887 	y = WI_DrawPatchText(y, &entering, "WI_ENTERING");
888 	y += entering.mFont->GetHeight() * CleanYfac / 4;
889 	WI_DrawName(y, wbs->LName1, lnametexts[1]);
890 }
891 
892 
893 //====================================================================
894 //
895 // Draws the splats and the 'You are here' arrows
896 //
897 //====================================================================
898 
WI_MapToIndex(const char * map)899 int WI_MapToIndex (const char *map)
900 {
901 	unsigned int i;
902 
903 	for (i = 0; i < lnodes.Size(); i++)
904 	{
905 		if (!strnicmp (lnodes[i].level, map, 8))
906 			break;
907 	}
908 	return i;
909 }
910 
911 
912 //====================================================================
913 //
914 // Draws the splats and the 'You are here' arrows
915 //
916 //====================================================================
917 
WI_drawOnLnode(int n,FTexture * c[],int numc)918 void WI_drawOnLnode( int   n, FTexture * c[] ,int numc)
919 {
920 	int   i;
921 	for(i=0;i<numc;i++)
922 	{
923 		int            left;
924 		int            top;
925 		int            right;
926 		int            bottom;
927 
928 
929 		right = c[i]->GetScaledWidth();
930 		bottom = c[i]->GetScaledHeight();
931 		left = lnodes[n].x - c[i]->GetScaledLeftOffset();
932 		top = lnodes[n].y - c[i]->GetScaledTopOffset();
933 		right += left;
934 		bottom += top;
935 
936 		if (left >= 0 && right < 320 && top >= 0 && bottom < 200)
937 		{
938 			screen->DrawTexture (c[i], lnodes[n].x, lnodes[n].y, DTA_320x200, true, TAG_DONE);
939 			break;
940 		}
941 	}
942 }
943 
944 //====================================================================
945 //
946 // Draws a number.
947 // If digits > 0, then use that many digits minimum,
948 //	otherwise only use as many as necessary.
949 // x is the right edge of the number.
950 // Returns new x position, that is, the left edge of the number.
951 //
952 //====================================================================
WI_drawNum(FFont * font,int x,int y,int n,int digits,bool leadingzeros=true,EColorRange translation=CR_UNTRANSLATED)953 int WI_drawNum (FFont *font, int x, int y, int n, int digits, bool leadingzeros=true, EColorRange translation=CR_UNTRANSLATED)
954 {
955 	int fontwidth = font->GetCharWidth('3');
956 	char text[8];
957 	int len;
958 	char *text_p;
959 	bool nomove = font != IntermissionFont;
960 
961 	if (nomove)
962 	{
963 		fontwidth *= CleanXfac;
964 	}
965 	if (leadingzeros)
966 	{
967 		len = mysnprintf (text, countof(text), "%0*d", digits, n);
968 	}
969 	else
970 	{
971 		len = mysnprintf (text, countof(text), "%d", n);
972 	}
973 	text_p = text + MIN<int>(len, countof(text)-1);
974 
975 	while (--text_p >= text)
976 	{
977 		// Digits are centered in a box the width of the '3' character.
978 		// Other characters (specifically, '-') are right-aligned in their cell.
979 		if (*text_p >= '0' && *text_p <= '9')
980 		{
981 			x -= fontwidth;
982 			WI_DrawCharPatch(font, *text_p, x + (fontwidth - font->GetCharWidth(*text_p)) / 2, y, translation, nomove);
983 		}
984 		else
985 		{
986 			WI_DrawCharPatch(font, *text_p, x - font->GetCharWidth(*text_p), y, translation, nomove);
987 			x -= fontwidth;
988 		}
989 	}
990 	if (len < digits)
991 	{
992 		x -= fontwidth * (digits - len);
993 	}
994 	return x;
995 }
996 
997 //====================================================================
998 //
999 //
1000 //
1001 //====================================================================
1002 
WI_drawPercent(FFont * font,int x,int y,int p,int b,bool show_total=true,EColorRange color=CR_UNTRANSLATED)1003 void WI_drawPercent (FFont *font, int x, int y, int p, int b, bool show_total=true, EColorRange color=CR_UNTRANSLATED)
1004 {
1005 	if (p < 0)
1006 		return;
1007 
1008 	if (wi_percents)
1009 	{
1010 		if (font != IntermissionFont)
1011 		{
1012 			x -= font->GetCharWidth('%') * CleanXfac;
1013 		}
1014 		else
1015 		{
1016 			x -= font->GetCharWidth('%');
1017 		}
1018 		screen->DrawText(font, color, x, y, "%", font != IntermissionFont ? DTA_CleanNoMove : DTA_Clean, true, TAG_DONE);
1019 		if (font != IntermissionFont)
1020 		{
1021 			x -= 2*CleanXfac;
1022 		}
1023 		WI_drawNum(font, x, y, b == 0 ? 100 : p * 100 / b, -1, false, color);
1024 	}
1025 	else
1026 	{
1027 		if (show_total)
1028 		{
1029 			x = WI_drawNum(font, x, y, b, 2, false);
1030 			x -= font->GetCharWidth('/');
1031 			screen->DrawText (IntermissionFont, color, x, y, "/",
1032 				DTA_Clean, true, TAG_DONE);
1033 		}
1034 		WI_drawNum (font, x, y, p, -1, false, color);
1035 	}
1036 }
1037 
1038 //====================================================================
1039 //
1040 // Display level completion time and par, or "sucks" message if overflow.
1041 //
1042 //====================================================================
WI_drawTime(int x,int y,int t,bool no_sucks=false)1043 void WI_drawTime (int x, int y, int t, bool no_sucks=false)
1044 {
1045 	bool sucky;
1046 
1047 	if (t<0)
1048 		return;
1049 
1050 	sucky = !no_sucks && t >= wbs->sucktime * 60 * 60 && wbs->sucktime > 0;
1051 
1052 	if (sucky)
1053 	{ // "sucks"
1054 		if (sucks != NULL)
1055 		{
1056 			screen->DrawTexture (sucks, x - sucks->GetScaledWidth(), y - IntermissionFont->GetHeight() - 2,
1057 				DTA_Clean, true, TAG_DONE);
1058 		}
1059 		else
1060 		{
1061 			screen->DrawText (BigFont, CR_UNTRANSLATED, x  - BigFont->StringWidth("SUCKS"), y - IntermissionFont->GetHeight() - 2,
1062 				"SUCKS", DTA_Clean, true, TAG_DONE);
1063 		}
1064 	}
1065 
1066 	int hours = t / 3600;
1067 	t -= hours * 3600;
1068 	int minutes = t / 60;
1069 	t -= minutes * 60;
1070 	int seconds = t;
1071 
1072 	// Why were these offsets hard coded? Half the WADs with custom patches
1073 	// I tested screwed up miserably in this function!
1074 	int num_spacing = IntermissionFont->GetCharWidth('3');
1075 	int colon_spacing = IntermissionFont->GetCharWidth(':');
1076 
1077 	x = WI_drawNum (IntermissionFont, x, y, seconds, 2) - 1;
1078 	WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y);
1079 	x = WI_drawNum (IntermissionFont, x, y, minutes, 2, hours!=0);
1080 	if (hours)
1081 	{
1082 		WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y);
1083 		WI_drawNum (IntermissionFont, x, y, hours, 2);
1084 	}
1085 }
1086 
WI_End()1087 void WI_End ()
1088 {
1089 	state = LeavingIntermission;
1090 	WI_unloadData ();
1091 
1092 	//Added by mc
1093 	if (deathmatch)
1094 	{
1095 		bglobal.RemoveAllBots (consoleplayer != Net_Arbitrator);
1096 	}
1097 }
1098 
WI_autoSkip()1099 bool WI_autoSkip()
1100 {
1101 	return wi_autoadvance > 0 && bcnt > (wi_autoadvance * TICRATE);
1102 }
1103 
WI_initNoState()1104 void WI_initNoState ()
1105 {
1106 	state = NoState;
1107 	acceleratestage = 0;
1108 	cnt = 10;
1109 }
1110 
WI_updateNoState()1111 void WI_updateNoState ()
1112 {
1113 	WI_updateAnimatedBack();
1114 
1115 	if (acceleratestage)
1116 	{
1117 		cnt = 0;
1118 	}
1119 	else
1120 	{
1121 		bool noauto = noautostartmap;
1122 		bool autoskip = WI_autoSkip();
1123 
1124 		for (int i = 0; !noauto && i < MAXPLAYERS; ++i)
1125 		{
1126 			if (playeringame[i])
1127 			{
1128 				noauto |= players[i].userinfo.GetNoAutostartMap();
1129 			}
1130 		}
1131 		if (!noauto || autoskip)
1132 		{
1133 			cnt--;
1134 		}
1135 	}
1136 
1137 	if (cnt == 0)
1138 	{
1139 		WI_End();
1140 		G_WorldDone();
1141 	}
1142 }
1143 
1144 static bool snl_pointeron = false;
1145 
WI_initShowNextLoc()1146 void WI_initShowNextLoc ()
1147 {
1148 	if (wbs->next_ep == -1)
1149 	{
1150 		// Last map in episode - there is no next location!
1151 		WI_End();
1152 		G_WorldDone();
1153 		return;
1154 	}
1155 
1156 	state = ShowNextLoc;
1157 	acceleratestage = 0;
1158 	cnt = SHOWNEXTLOCDELAY * TICRATE;
1159 	WI_LoadBackground(true);
1160 }
1161 
WI_updateShowNextLoc()1162 void WI_updateShowNextLoc ()
1163 {
1164 	WI_updateAnimatedBack();
1165 
1166 	if (!--cnt || acceleratestage)
1167 		WI_initNoState();
1168 	else
1169 		snl_pointeron = (cnt & 31) < 20;
1170 }
1171 
WI_drawShowNextLoc(void)1172 void WI_drawShowNextLoc(void)
1173 {
1174 	unsigned int i;
1175 
1176 	WI_drawBackground();
1177 
1178 	if (splat)
1179 	{
1180 		for (i=0 ; i<lnodes.Size() ; i++)
1181 		{
1182 			level_info_t * li = FindLevelInfo (lnodes[i].level);
1183 			if (li && li->flags & LEVEL_VISITED) WI_drawOnLnode(i, &splat,1);  // draw a splat on taken cities.
1184 		}
1185 	}
1186 
1187 	// draw flashing ptr
1188 	if (snl_pointeron && yah.Size())
1189 	{
1190 		unsigned int v = WI_MapToIndex (wbs->next);
1191 		// Draw only if it points to a valid level on the current screen!
1192 		if (v<lnodes.Size()) WI_drawOnLnode (v, &yah[0], yah.Size());
1193 	}
1194 
1195 	// draws which level you are entering..
1196 	WI_drawEL ();
1197 
1198 }
1199 
WI_drawNoState()1200 void WI_drawNoState ()
1201 {
1202 	snl_pointeron = true;
1203 	WI_drawShowNextLoc();
1204 }
1205 
WI_fragSum(int playernum)1206 int WI_fragSum (int playernum)
1207 {
1208 	int i;
1209 	int frags = 0;
1210 
1211 	for (i = 0; i < MAXPLAYERS; i++)
1212 	{
1213 		if (playeringame[i]
1214 			&& i!=playernum)
1215 		{
1216 			frags += plrs[playernum].frags[i];
1217 		}
1218 	}
1219 
1220 	// JDC hack - negative frags.
1221 	frags -= plrs[playernum].frags[playernum];
1222 
1223 	return frags;
1224 }
1225 
1226 static int player_deaths[MAXPLAYERS];
1227 
WI_initDeathmatchStats(void)1228 void WI_initDeathmatchStats (void)
1229 {
1230 	int i, j;
1231 
1232 	state = StatCount;
1233 	acceleratestage = 0;
1234 	memset(playerready, 0, sizeof(playerready));
1235 	memset(cnt_frags, 0, sizeof(cnt_frags));
1236 	memset(cnt_deaths, 0, sizeof(cnt_deaths));
1237 	memset(player_deaths, 0, sizeof(player_deaths));
1238 	total_frags = 0;
1239 	total_deaths = 0;
1240 
1241 	ng_state = 1;
1242 	cnt_pause = TICRATE;
1243 
1244 	for (i=0 ; i<MAXPLAYERS ; i++)
1245 	{
1246 		if (playeringame[i])
1247 		{
1248 			for (j = 0; j < MAXPLAYERS; j++)
1249 				if (playeringame[j])
1250 					player_deaths[i] += plrs[j].frags[i];
1251 			total_deaths += player_deaths[i];
1252 			total_frags += plrs[i].fragcount;
1253 		}
1254 	}
1255 }
1256 
WI_updateDeathmatchStats()1257 void WI_updateDeathmatchStats ()
1258 {
1259 
1260 	int i;
1261 	bool stillticking;
1262 	bool autoskip = WI_autoSkip();
1263 
1264 	WI_updateAnimatedBack();
1265 
1266 	if ((acceleratestage || autoskip) && ng_state != 6)
1267 	{
1268 		acceleratestage = 0;
1269 
1270 		for (i = 0; i<MAXPLAYERS; i++)
1271 		{
1272 			if (!playeringame[i])
1273 				continue;
1274 
1275 			cnt_frags[i] = plrs[i].fragcount;
1276 			cnt_deaths[i] = player_deaths[i];
1277 		}
1278 		S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1279 		ng_state = 6;
1280 	}
1281 
1282 	if (ng_state == 2)
1283 	{
1284 		if (!(bcnt & 3))
1285 			S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1286 
1287 		stillticking = false;
1288 
1289 		for (i = 0; i<MAXPLAYERS; i++)
1290 		{
1291 			if (!playeringame[i])
1292 				continue;
1293 
1294 			cnt_frags[i] += 2;
1295 
1296 			if (cnt_frags[i] > plrs[i].fragcount)
1297 				cnt_frags[i] = plrs[i].fragcount;
1298 			else
1299 				stillticking = true;
1300 		}
1301 
1302 		if (!stillticking)
1303 		{
1304 			S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1305 			ng_state++;
1306 		}
1307 	}
1308 	else if (ng_state == 4)
1309 	{
1310 		if (!(bcnt & 3))
1311 			S_Sound(CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1312 
1313 		stillticking = false;
1314 
1315 		for (i = 0; i<MAXPLAYERS; i++)
1316 		{
1317 			if (!playeringame[i])
1318 				continue;
1319 
1320 			cnt_deaths[i] += 2;
1321 			if (cnt_deaths[i] > player_deaths[i])
1322 				cnt_deaths[i] = player_deaths[i];
1323 			else
1324 				stillticking = true;
1325 		}
1326 		if (!stillticking)
1327 		{
1328 			S_Sound(CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1329 			ng_state++;
1330 		}
1331 	}
1332 	else if (ng_state == 6)
1333 	{
1334 		int i;
1335 		for (i = 0; i < MAXPLAYERS; i++)
1336 		{
1337 			// If the player is in the game and not ready, stop checking
1338 			if (playeringame[i] && players[i].Bot == NULL && !playerready[i])
1339 				break;
1340 		}
1341 
1342 		// All players are ready; proceed.
1343 		if ((i == MAXPLAYERS && acceleratestage) || autoskip)
1344 		{
1345 			S_Sound(CHAN_VOICE | CHAN_UI, "intermission/pastdmstats", 1, ATTN_NONE);
1346 			WI_initShowNextLoc();
1347 		}
1348 	}
1349 	else if (ng_state & 1)
1350 	{
1351 		if (!--cnt_pause)
1352 		{
1353 			ng_state++;
1354 			cnt_pause = TICRATE;
1355 		}
1356 	}
1357 }
1358 
1359 
1360 
WI_drawDeathmatchStats()1361 void WI_drawDeathmatchStats ()
1362 {
1363 	int i, pnum, x, y, ypadding, height, lineheight;
1364 	int maxnamewidth, maxscorewidth, maxiconheight;
1365 	int pwidth = IntermissionFont->GetCharWidth('%');
1366 	int icon_x, name_x, frags_x, deaths_x;
1367 	int deaths_len;
1368 	float h, s, v, r, g, b;
1369 	EColorRange color;
1370 	const char *text_deaths, *text_frags;
1371 	FTexture *readyico = TexMan.FindTexture("READYICO");
1372 	player_t *sortedplayers[MAXPLAYERS];
1373 
1374 	// draw animated background
1375 	WI_drawBackground();
1376 
1377 	y = WI_drawLF();
1378 
1379 	HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight);
1380 	// Use the readyico height if it's bigger.
1381 	height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset();
1382 	maxiconheight = MAX(height, maxiconheight);
1383 	height = SmallFont->GetHeight() * CleanYfac;
1384 	lineheight = MAX(height, maxiconheight * CleanYfac);
1385 	ypadding = (lineheight - height + 1) / 2;
1386 	y += CleanYfac;
1387 
1388 	text_deaths = GStrings("SCORE_DEATHS");
1389 	//text_color = GStrings("SCORE_COLOR");
1390 	text_frags = GStrings("SCORE_FRAGS");
1391 
1392 	icon_x = 8 * CleanXfac;
1393 	name_x = icon_x + maxscorewidth * CleanXfac;
1394 	frags_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_frags)) + 8) * CleanXfac;
1395 	deaths_x = frags_x + ((deaths_len = SmallFont->StringWidth(text_deaths)) + 8) * CleanXfac;
1396 
1397 	x = (SCREENWIDTH - deaths_x) >> 1;
1398 	icon_x += x;
1399 	name_x += x;
1400 	frags_x += x;
1401 	deaths_x += x;
1402 
1403 	color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
1404 
1405 	screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE);
1406 	screen->DrawText(SmallFont, color, frags_x - SmallFont->StringWidth(text_frags)*CleanXfac, y, text_frags, DTA_CleanNoMove, true, TAG_DONE);
1407 	screen->DrawText(SmallFont, color, deaths_x - deaths_len*CleanXfac, y, text_deaths, DTA_CleanNoMove, true, TAG_DONE);
1408 	y += height + 6 * CleanYfac;
1409 
1410 	// Sort all players
1411 	for (i = 0; i < MAXPLAYERS; i++)
1412 	{
1413 		sortedplayers[i] = &players[i];
1414 	}
1415 
1416 	if (teamplay)
1417 		qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), compareteams);
1418 	else
1419 		qsort(sortedplayers, MAXPLAYERS, sizeof(player_t *), comparepoints);
1420 
1421 	// Draw lines for each player
1422 	for (i = 0; i < MAXPLAYERS; i++)
1423 	{
1424 		player_t *player = sortedplayers[i];
1425 		pnum = int(player - players);
1426 
1427 		if (!playeringame[pnum])
1428 			continue;
1429 
1430 		D_GetPlayerColor(pnum, &h, &s, &v, NULL);
1431 		HSVtoRGB(&r, &g, &b, h, s, v);
1432 
1433 		screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255),
1434 			clamp(int(g*255.f), 0, 255),
1435 			clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (deaths_x - x) + (8 * CleanXfac), lineheight);
1436 
1437 		if (playerready[pnum] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion
1438 			screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE);
1439 
1440 		color = (EColorRange)HU_GetRowColor(player, pnum == consoleplayer);
1441 		if (player->mo->ScoreIcon.isValid())
1442 		{
1443 			FTexture *pic = TexMan[player->mo->ScoreIcon];
1444 			screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE);
1445 		}
1446 		screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE);
1447 		WI_drawNum(SmallFont, frags_x, y + ypadding, cnt_frags[pnum], 0, false, color);
1448 		if (ng_state >= 2)
1449 		{
1450 			WI_drawNum(SmallFont, deaths_x, y + ypadding, cnt_deaths[pnum], 0, false, color);
1451 		}
1452 		y += lineheight + CleanYfac;
1453 	}
1454 
1455 	// Draw "TOTAL" line
1456 	y += height + 3 * CleanYfac;
1457 	color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
1458 	screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE);
1459 	WI_drawNum(SmallFont, frags_x, y, total_frags, 0, false, color);
1460 	if (ng_state >= 4)
1461 	{
1462 		WI_drawNum(SmallFont, deaths_x, y, total_deaths, 0, false, color);
1463 	}
1464 
1465 	// Draw game time
1466 	y += height + CleanYfac;
1467 
1468 	int seconds = Tics2Seconds(plrs[me].stime);
1469 	int hours = seconds / 3600;
1470 	int minutes = (seconds % 3600) / 60;
1471 	seconds = seconds % 60;
1472 
1473 	FString leveltime = GStrings("SCORE_LVLTIME");
1474 	leveltime += ": ";
1475 
1476 	char timer[sizeof "HH:MM:SS"];
1477 	mysnprintf(timer, sizeof(timer), "%02i:%02i:%02i", hours, minutes, seconds);
1478 	leveltime += timer;
1479 
1480 	screen->DrawText(SmallFont, color, x, y, leveltime, DTA_CleanNoMove, true, TAG_DONE);
1481 }
1482 
WI_initNetgameStats()1483 void WI_initNetgameStats ()
1484 {
1485 
1486 	int i;
1487 
1488 	state = StatCount;
1489 	acceleratestage = 0;
1490 	memset(playerready, 0, sizeof(playerready));
1491 	ng_state = 1;
1492 
1493 	cnt_pause = TICRATE;
1494 
1495 	for (i = 0; i < MAXPLAYERS; i++)
1496 	{
1497 		if (!playeringame[i])
1498 			continue;
1499 
1500 		cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;
1501 
1502 		dofrags += WI_fragSum (i);
1503 	}
1504 
1505 	dofrags = !!dofrags;
1506 }
1507 
WI_updateNetgameStats()1508 void WI_updateNetgameStats ()
1509 {
1510 
1511 	int i;
1512 	int fsum;
1513 	bool stillticking;
1514 	bool autoskip = WI_autoSkip();
1515 
1516 	WI_updateAnimatedBack ();
1517 
1518 	if ((acceleratestage || autoskip) && ng_state != 10)
1519 	{
1520 		acceleratestage = 0;
1521 
1522 		for (i=0 ; i<MAXPLAYERS ; i++)
1523 		{
1524 			if (!playeringame[i])
1525 				continue;
1526 
1527 			cnt_kills[i] = plrs[i].skills;
1528 			cnt_items[i] = plrs[i].sitems;
1529 			cnt_secret[i] = plrs[i].ssecret;
1530 
1531 			if (dofrags)
1532 				cnt_frags[i] = WI_fragSum (i);
1533 		}
1534 		S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1535 		ng_state = 10;
1536 	}
1537 
1538 	if (ng_state == 2)
1539 	{
1540 		if (!(bcnt&3))
1541 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1542 
1543 		stillticking = false;
1544 
1545 		for (i=0 ; i<MAXPLAYERS ; i++)
1546 		{
1547 			if (!playeringame[i])
1548 				continue;
1549 
1550 			cnt_kills[i] += 2;
1551 
1552 			if (cnt_kills[i] > plrs[i].skills)
1553 				cnt_kills[i] = plrs[i].skills;
1554 			else
1555 				stillticking = true;
1556 		}
1557 
1558 		if (!stillticking)
1559 		{
1560 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1561 			ng_state++;
1562 		}
1563 	}
1564 	else if (ng_state == 4)
1565 	{
1566 		if (!(bcnt&3))
1567 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1568 
1569 		stillticking = false;
1570 
1571 		for (i=0 ; i<MAXPLAYERS ; i++)
1572 		{
1573 			if (!playeringame[i])
1574 				continue;
1575 
1576 			cnt_items[i] += 2;
1577 			if (cnt_items[i] > plrs[i].sitems)
1578 				cnt_items[i] = plrs[i].sitems;
1579 			else
1580 				stillticking = true;
1581 		}
1582 		if (!stillticking)
1583 		{
1584 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1585 			ng_state++;
1586 		}
1587 	}
1588 	else if (ng_state == 6)
1589 	{
1590 		if (!(bcnt&3))
1591 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1592 
1593 		stillticking = false;
1594 
1595 		for (i=0 ; i<MAXPLAYERS ; i++)
1596 		{
1597 			if (!playeringame[i])
1598 				continue;
1599 
1600 			cnt_secret[i] += 2;
1601 
1602 			if (cnt_secret[i] > plrs[i].ssecret)
1603 				cnt_secret[i] = plrs[i].ssecret;
1604 			else
1605 				stillticking = true;
1606 		}
1607 
1608 		if (!stillticking)
1609 		{
1610 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1611 			ng_state += 1 + 2*!dofrags;
1612 		}
1613 	}
1614 	else if (ng_state == 8)
1615 	{
1616 		if (!(bcnt&3))
1617 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1618 
1619 		stillticking = false;
1620 
1621 		for (i=0 ; i<MAXPLAYERS ; i++)
1622 		{
1623 			if (!playeringame[i])
1624 				continue;
1625 
1626 			cnt_frags[i] += 1;
1627 
1628 			if (cnt_frags[i] >= (fsum = WI_fragSum(i)))
1629 				cnt_frags[i] = fsum;
1630 			else
1631 				stillticking = true;
1632 		}
1633 
1634 		if (!stillticking)
1635 		{
1636 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/cooptotal", 1, ATTN_NONE);
1637 			ng_state++;
1638 		}
1639 	}
1640 	else if (ng_state == 10)
1641 	{
1642 		int i;
1643 		for (i = 0; i < MAXPLAYERS; i++)
1644 		{
1645 			// If the player is in the game and not ready, stop checking
1646 			if (playeringame[i] && players[i].Bot == NULL && !playerready[i])
1647 				break;
1648 		}
1649 
1650 		// All players are ready; proceed.
1651 		if ((i == MAXPLAYERS && acceleratestage) || autoskip)
1652 		{
1653 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/pastcoopstats", 1, ATTN_NONE);
1654 			WI_initShowNextLoc();
1655 		}
1656 	}
1657 	else if (ng_state & 1)
1658 	{
1659 		if (!--cnt_pause)
1660 		{
1661 			ng_state++;
1662 			cnt_pause = TICRATE;
1663 		}
1664 	}
1665 }
1666 
WI_drawNetgameStats()1667 void WI_drawNetgameStats ()
1668 {
1669 	int i, x, y, ypadding, height, lineheight;
1670 	int maxnamewidth, maxscorewidth, maxiconheight;
1671 	int pwidth = IntermissionFont->GetCharWidth('%');
1672 	int icon_x, name_x, kills_x, bonus_x, secret_x;
1673 	int bonus_len, secret_len;
1674 	int missed_kills, missed_items, missed_secrets;
1675 	float h, s, v, r, g, b;
1676 	EColorRange color;
1677 	const char *text_bonus, *text_secret, *text_kills;
1678 	FTexture *readyico = TexMan.FindTexture("READYICO");
1679 
1680 	// draw animated background
1681 	WI_drawBackground();
1682 
1683 	y = WI_drawLF();
1684 
1685 	HU_GetPlayerWidths(maxnamewidth, maxscorewidth, maxiconheight);
1686 	// Use the readyico height if it's bigger.
1687 	height = readyico->GetScaledHeight() - readyico->GetScaledTopOffset();
1688 	if (height > maxiconheight)
1689 	{
1690 		maxiconheight = height;
1691 	}
1692 	height = SmallFont->GetHeight() * CleanYfac;
1693 	lineheight = MAX(height, maxiconheight * CleanYfac);
1694 	ypadding = (lineheight - height + 1) / 2;
1695 	y += CleanYfac;
1696 
1697 	text_bonus = GStrings((gameinfo.gametype & GAME_Raven) ? "SCORE_BONUS" : "SCORE_ITEMS");
1698 	text_secret = GStrings("SCORE_SECRET");
1699 	text_kills = GStrings("SCORE_KILLS");
1700 
1701 	icon_x = 8 * CleanXfac;
1702 	name_x = icon_x + maxscorewidth * CleanXfac;
1703 	kills_x = name_x + (maxnamewidth + MAX(SmallFont->StringWidth("XXXXX"), SmallFont->StringWidth(text_kills)) + 8) * CleanXfac;
1704 	bonus_x = kills_x + ((bonus_len = SmallFont->StringWidth(text_bonus)) + 8) * CleanXfac;
1705 	secret_x = bonus_x + ((secret_len = SmallFont->StringWidth(text_secret)) + 8) * CleanXfac;
1706 
1707 	x = (SCREENWIDTH - secret_x) >> 1;
1708 	icon_x += x;
1709 	name_x += x;
1710 	kills_x += x;
1711 	bonus_x += x;
1712 	secret_x += x;
1713 
1714 	color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
1715 
1716 	screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_NAME"), DTA_CleanNoMove, true, TAG_DONE);
1717 	screen->DrawText(SmallFont, color, kills_x - SmallFont->StringWidth(text_kills)*CleanXfac, y, text_kills, DTA_CleanNoMove, true, TAG_DONE);
1718 	screen->DrawText(SmallFont, color, bonus_x - bonus_len*CleanXfac, y, text_bonus, DTA_CleanNoMove, true, TAG_DONE);
1719 	screen->DrawText(SmallFont, color, secret_x - secret_len*CleanXfac, y, text_secret, DTA_CleanNoMove, true, TAG_DONE);
1720 	y += height + 6 * CleanYfac;
1721 
1722 	missed_kills = wbs->maxkills;
1723 	missed_items = wbs->maxitems;
1724 	missed_secrets = wbs->maxsecret;
1725 
1726 	// Draw lines for each player
1727 	for (i = 0; i < MAXPLAYERS; ++i)
1728 	{
1729 		player_t *player;
1730 
1731 		if (!playeringame[i])
1732 			continue;
1733 
1734 		player = &players[i];
1735 
1736 		D_GetPlayerColor(i, &h, &s, &v, NULL);
1737 		HSVtoRGB(&r, &g, &b, h, s, v);
1738 
1739 		screen->Dim(MAKERGB(clamp(int(r*255.f), 0, 255),
1740 			clamp(int(g*255.f), 0, 255),
1741 			clamp(int(b*255.f), 0, 255)), 0.8f, x, y - ypadding, (secret_x - x) + (8 * CleanXfac), lineheight);
1742 
1743 		if (playerready[i] || player->Bot != NULL) // Bots are automatically assumed ready, to prevent confusion
1744 			screen->DrawTexture(readyico, x - (readyico->GetWidth() * CleanXfac), y, DTA_CleanNoMove, true, TAG_DONE);
1745 
1746 		color = (EColorRange)HU_GetRowColor(player, i == consoleplayer);
1747 		if (player->mo->ScoreIcon.isValid())
1748 		{
1749 			FTexture *pic = TexMan[player->mo->ScoreIcon];
1750 			screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE);
1751 		}
1752 		screen->DrawText(SmallFont, color, name_x, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE);
1753 		WI_drawPercent(SmallFont, kills_x, y + ypadding, cnt_kills[i], wbs->maxkills, false, color);
1754 		missed_kills -= cnt_kills[i];
1755 		if (ng_state >= 4)
1756 		{
1757 			WI_drawPercent(SmallFont, bonus_x, y + ypadding, cnt_items[i], wbs->maxitems, false, color);
1758 			missed_items -= cnt_items[i];
1759 			if (ng_state >= 6)
1760 			{
1761 				WI_drawPercent(SmallFont, secret_x, y + ypadding, cnt_secret[i], wbs->maxsecret, false, color);
1762 				missed_secrets -= cnt_secret[i];
1763 			}
1764 		}
1765 		y += lineheight + CleanYfac;
1766 	}
1767 
1768 	// Draw "MISSED" line
1769 	y += 3 * CleanYfac;
1770 	screen->DrawText(SmallFont, CR_DARKGRAY, name_x, y, GStrings("SCORE_MISSED"), DTA_CleanNoMove, true, TAG_DONE);
1771 	WI_drawPercent(SmallFont, kills_x, y, missed_kills, wbs->maxkills, false, CR_DARKGRAY);
1772 	if (ng_state >= 4)
1773 	{
1774 		WI_drawPercent(SmallFont, bonus_x, y, missed_items, wbs->maxitems, false, CR_DARKGRAY);
1775 		if (ng_state >= 6)
1776 		{
1777 			WI_drawPercent(SmallFont, secret_x, y, missed_secrets, wbs->maxsecret, false, CR_DARKGRAY);
1778 		}
1779 	}
1780 
1781 	// Draw "TOTAL" line
1782 	y += height + 3 * CleanYfac;
1783 	color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
1784 	screen->DrawText(SmallFont, color, name_x, y, GStrings("SCORE_TOTAL"), DTA_CleanNoMove, true, TAG_DONE);
1785 	WI_drawNum(SmallFont, kills_x, y, wbs->maxkills, 0, false, color);
1786 	if (ng_state >= 4)
1787 	{
1788 		WI_drawNum(SmallFont, bonus_x, y, wbs->maxitems, 0, false, color);
1789 		if (ng_state >= 6)
1790 		{
1791 			WI_drawNum(SmallFont, secret_x, y, wbs->maxsecret, 0, false, color);
1792 		}
1793 	}
1794 }
1795 
1796 static int  sp_state;
1797 
WI_initStats()1798 void WI_initStats ()
1799 {
1800 	state = StatCount;
1801 	acceleratestage = 0;
1802 	sp_state = 1;
1803 	cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
1804 	cnt_time = cnt_par = -1;
1805 	cnt_pause = TICRATE;
1806 
1807 	cnt_total_time = -1;
1808 }
1809 
WI_updateStats()1810 void WI_updateStats ()
1811 {
1812 	WI_updateAnimatedBack ();
1813 
1814 	if (acceleratestage && sp_state != 10)
1815 	{
1816 		acceleratestage = 0;
1817 		sp_state = 10;
1818 		S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1819 
1820 		cnt_kills[0] = plrs[me].skills;
1821 		cnt_items[0] = plrs[me].sitems;
1822 		cnt_secret[0] = plrs[me].ssecret;
1823 		cnt_time = Tics2Seconds(plrs[me].stime);
1824 		cnt_par = wbs->partime / TICRATE;
1825 		cnt_total_time = Tics2Seconds(wbs->totaltime);
1826 	}
1827 
1828 	if (sp_state == 2)
1829 	{
1830 		if (gameinfo.intermissioncounter)
1831 		{
1832 			cnt_kills[0] += 2;
1833 
1834 			if (!(bcnt&3))
1835 				S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1836 		}
1837 		if (!gameinfo.intermissioncounter || cnt_kills[0] >= plrs[me].skills)
1838 		{
1839 			cnt_kills[0] = plrs[me].skills;
1840 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1841 			sp_state++;
1842 		}
1843 	}
1844 	else if (sp_state == 4)
1845 	{
1846 		if (gameinfo.intermissioncounter)
1847 		{
1848 			cnt_items[0] += 2;
1849 
1850 			if (!(bcnt&3))
1851 				S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1852 		}
1853 		if (!gameinfo.intermissioncounter || cnt_items[0] >= plrs[me].sitems)
1854 		{
1855 			cnt_items[0] = plrs[me].sitems;
1856 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1857 			sp_state++;
1858 		}
1859 	}
1860 	else if (sp_state == 6)
1861 	{
1862 		if (gameinfo.intermissioncounter)
1863 		{
1864 			cnt_secret[0] += 2;
1865 
1866 			if (!(bcnt&3))
1867 				S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1868 		}
1869 		if (!gameinfo.intermissioncounter || cnt_secret[0] >= plrs[me].ssecret)
1870 		{
1871 			cnt_secret[0] = plrs[me].ssecret;
1872 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1873 			sp_state++;
1874 		}
1875 	}
1876 	else if (sp_state == 8)
1877 	{
1878 		if (gameinfo.intermissioncounter)
1879 		{
1880 			if (!(bcnt&3))
1881 				S_Sound (CHAN_VOICE | CHAN_UI, "intermission/tick", 1, ATTN_NONE);
1882 
1883 			cnt_time += 3;
1884 			cnt_par += 3;
1885 			cnt_total_time += 3;
1886 		}
1887 
1888 		int sec = Tics2Seconds(plrs[me].stime);
1889 		if (!gameinfo.intermissioncounter || cnt_time >= sec)
1890 			cnt_time = sec;
1891 
1892 		int tsec = Tics2Seconds(wbs->totaltime);
1893 		if (!gameinfo.intermissioncounter || cnt_total_time >= tsec)
1894 			cnt_total_time = tsec;
1895 
1896 		if (!gameinfo.intermissioncounter || cnt_par >= wbs->partime / TICRATE)
1897 		{
1898 			cnt_par = wbs->partime / TICRATE;
1899 
1900 			if (cnt_time >= sec)
1901 			{
1902 				cnt_total_time = tsec;
1903 				S_Sound (CHAN_VOICE | CHAN_UI, "intermission/nextstage", 1, ATTN_NONE);
1904 				sp_state++;
1905 			}
1906 		}
1907 	}
1908 	else if (sp_state == 10)
1909 	{
1910 		if (acceleratestage)
1911 		{
1912 			S_Sound (CHAN_VOICE | CHAN_UI, "intermission/paststats", 1, ATTN_NONE);
1913 			WI_initShowNextLoc();
1914 		}
1915 	}
1916 	else if (sp_state & 1)
1917 	{
1918 		if (!--cnt_pause)
1919 		{
1920 			sp_state++;
1921 			cnt_pause = TICRATE;
1922 		}
1923 	}
1924 }
1925 
WI_drawStats(void)1926 void WI_drawStats (void)
1927 {
1928 	// line height
1929 	int lh;
1930 
1931 	lh = IntermissionFont->GetHeight() * 3 / 2;
1932 
1933 	// draw animated background
1934 	WI_drawBackground();
1935 
1936 	WI_drawLF();
1937 
1938 	if (gameinfo.gametype & GAME_DoomChex)
1939 	{
1940 		screen->DrawTexture (kills, SP_STATSX, SP_STATSY, DTA_Clean, true, TAG_DONE);
1941 		WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY, cnt_kills[0], wbs->maxkills);
1942 
1943 		screen->DrawTexture (items, SP_STATSX, SP_STATSY+lh, DTA_Clean, true, TAG_DONE);
1944 		WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+lh, cnt_items[0], wbs->maxitems);
1945 
1946 		screen->DrawTexture (sp_secret, SP_STATSX, SP_STATSY+2*lh, DTA_Clean, true, TAG_DONE);
1947 		WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0], wbs->maxsecret);
1948 
1949 		screen->DrawTexture (timepic, SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
1950 		WI_drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time);
1951 		if (wi_showtotaltime)
1952 		{
1953 			WI_drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true);	// no 'sucks' for total time ever!
1954 		}
1955 
1956 		if (wbs->partime)
1957 		{
1958 			screen->DrawTexture (par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
1959 			WI_drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par);
1960 		}
1961 
1962 	}
1963 	else
1964 	{
1965 		screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, GStrings("TXT_IMKILLS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
1966 		screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, GStrings("TXT_IMITEMS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
1967 		screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, GStrings("TXT_IMSECRETS"), DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
1968 
1969 		int countpos = gameinfo.gametype==GAME_Strife? 285:270;
1970 		if (sp_state >= 2)
1971 		{
1972 			WI_drawPercent (IntermissionFont, countpos, 65, cnt_kills[0], wbs->maxkills);
1973 		}
1974 		if (sp_state >= 4)
1975 		{
1976 			WI_drawPercent (IntermissionFont, countpos, 90, cnt_items[0], wbs->maxitems);
1977 		}
1978 		if (sp_state >= 6)
1979 		{
1980 			WI_drawPercent (IntermissionFont, countpos, 115, cnt_secret[0], wbs->maxsecret);
1981 		}
1982 		if (sp_state >= 8)
1983 		{
1984 			screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, GStrings("TXT_IMTIME"),
1985 				DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
1986 			WI_drawTime (249, 160, cnt_time);
1987 			if (wi_showtotaltime)
1988 			{
1989 				WI_drawTime (249, 180, cnt_total_time);
1990 			}
1991 		}
1992 	}
1993 }
1994 
1995 // ====================================================================
1996 // WI_checkForAccelerate
1997 // Purpose: See if the player has hit either the attack or use key
1998 //          or mouse button.  If so we set acceleratestage to 1 and
1999 //          all those display routines above jump right to the end.
2000 // Args:    none
2001 // Returns: void
2002 //
2003 // ====================================================================
WI_checkForAccelerate(void)2004 void WI_checkForAccelerate(void)
2005 {
2006 	int i;
2007 	player_t *player;
2008 
2009 	// check for button presses to skip delays
2010 	for (i = 0, player = players; i < MAXPLAYERS; i++, player++)
2011 	{
2012 		if (playeringame[i])
2013 		{
2014 			if ((player->cmd.ucmd.buttons ^ player->oldbuttons) &&
2015 				((players[i].cmd.ucmd.buttons & players[i].oldbuttons)
2016 					== players[i].oldbuttons) && player->Bot == NULL)
2017 			{
2018 				acceleratestage = 1;
2019 				playerready[i] = true;
2020 			}
2021 			player->oldbuttons = player->cmd.ucmd.buttons;
2022 		}
2023 	}
2024 }
2025 
2026 // ====================================================================
2027 // WI_Ticker
2028 // Purpose: Do various updates every gametic, for stats, animation,
2029 //          checking that intermission music is running, etc.
2030 // Args:    none
2031 // Returns: void
2032 //
2033 // ====================================================================
WI_Ticker(void)2034 void WI_Ticker(void)
2035 {
2036 	// counter for general background animation
2037 	bcnt++;
2038 
2039 	if (bcnt == 1)
2040 	{
2041 		// intermission music - use the defaults if none specified
2042 		if (level.info->InterMusic.IsNotEmpty())
2043 			S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder);
2044 		else
2045 			S_ChangeMusic (gameinfo.intermissionMusic.GetChars(), gameinfo.intermissionOrder);
2046 
2047 	}
2048 
2049 	WI_checkForAccelerate();
2050 
2051 	switch (state)
2052 	{
2053     case StatCount:
2054 		if (deathmatch) WI_updateDeathmatchStats();
2055 		else if (multiplayer) WI_updateNetgameStats();
2056 		else WI_updateStats();
2057 		break;
2058 
2059     case ShowNextLoc:
2060 		WI_updateShowNextLoc();
2061 		break;
2062 
2063     case NoState:
2064 		WI_updateNoState();
2065 		break;
2066 
2067 	case LeavingIntermission:
2068 		// Hush, GCC.
2069 		break;
2070 	}
2071 }
2072 
2073 
WI_loadData(void)2074 void WI_loadData(void)
2075 {
2076 	entering.Init(gameinfo.mStatscreenEnteringFont);
2077 	finished.Init(gameinfo.mStatscreenFinishedFont);
2078 	mapname.Init(gameinfo.mStatscreenMapNameFont);
2079 
2080 	if (gameinfo.gametype & GAME_DoomChex)
2081 	{
2082 		kills = TexMan["WIOSTK"];		// "kills"
2083 		secret = TexMan["WIOSTS"];		// "scrt"
2084 		sp_secret = TexMan["WISCRT2"];	// "secret"
2085 		items = TexMan["WIOSTI"];		// "items"
2086 		frags = TexMan["WIFRGS"];		// "frgs"
2087 		timepic = TexMan["WITIME"];		// "time"
2088 		sucks = TexMan["WISUCKS"];		// "sucks"
2089 		par = TexMan["WIPAR"];			// "par"
2090 		killers = TexMan["WIKILRS"];	// "killers" (vertical]
2091 		victims = TexMan["WIVCTMS"];	// "victims" (horiz]
2092 		total = TexMan["WIMSTT"];		// "total"
2093 //		star = TexMan["STFST01"];		// your face
2094 //		bstar = TexMan["STFDEAD0"];		// dead face
2095  		p = TexMan["STPBANY"];
2096 	}
2097 #if 0
2098 	else if (gameinfo.gametype & GAME_Raven)
2099 	{
2100 		if (gameinfo.gametype == GAME_Heretic)
2101 		{
2102 			star = TexMan["FACEA0"];
2103 			bstar = TexMan["FACEB0"];
2104 		}
2105 		else
2106 		{
2107 			star = BigFont->GetChar('*', NULL);
2108 			bstar = star;
2109 		}
2110 	}
2111 	else // Strife needs some handling, too!
2112 	{
2113 		star = BigFont->GetChar('*', NULL);
2114 		bstar = star;
2115 	}
2116 #endif
2117 
2118 	// Use the local level structure which can be overridden by hubs
2119 	lnametexts[0] = level.LevelName;
2120 
2121 	level_info_t *li = FindLevelInfo(wbs->next);
2122 	if (li) lnametexts[1] = li->LookupLevelName();
2123 	else lnametexts[1] = "";
2124 
2125 	WI_LoadBackground(false);
2126 }
2127 
WI_unloadData()2128 void WI_unloadData ()
2129 {
2130 	// [RH] The texture data gets unloaded at pre-map time, so there's nothing to do here
2131 	return;
2132 }
2133 
WI_Drawer(void)2134 void WI_Drawer (void)
2135 {
2136 	switch (state)
2137 	{
2138 	case StatCount:
2139 		if (deathmatch)
2140 			WI_drawDeathmatchStats();
2141 		else if (multiplayer)
2142 			WI_drawNetgameStats();
2143 		else
2144 			WI_drawStats();
2145 		break;
2146 
2147 	case ShowNextLoc:
2148 		WI_drawShowNextLoc();
2149 		break;
2150 
2151 	case LeavingIntermission:
2152 		break;
2153 
2154 	default:
2155 		WI_drawNoState();
2156 		break;
2157 	}
2158 }
2159 
2160 
WI_initVariables(wbstartstruct_t * wbstartstruct)2161 void WI_initVariables (wbstartstruct_t *wbstartstruct)
2162 {
2163 	wbs = wbstartstruct;
2164 	acceleratestage = 0;
2165 	cnt = bcnt = 0;
2166 	me = wbs->pnum;
2167 	plrs = wbs->plyr;
2168 }
2169 
WI_Start(wbstartstruct_t * wbstartstruct)2170 void WI_Start (wbstartstruct_t *wbstartstruct)
2171 {
2172 	noautostartmap = false;
2173 	V_SetBlend (0,0,0,0);
2174 	WI_initVariables (wbstartstruct);
2175 	WI_loadData ();
2176 	if (deathmatch)
2177 		WI_initDeathmatchStats();
2178 	else if (multiplayer)
2179 		WI_initNetgameStats();
2180 	else
2181 		WI_initStats();
2182 	S_StopAllChannels ();
2183 	SN_StopAllSequences ();
2184 }
2185