1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 //	Intermission screens.
17 //
18 
19 // haleyjd 08/23/2010: There is no intermission in Strife
20 #if 0
21 #include <stdio.h>
22 
23 #include "z_zone.h"
24 
25 #include "m_random.h"
26 
27 #include "deh_main.h"
28 #include "i_swap.h"
29 #include "i_system.h"
30 
31 #include "w_wad.h"
32 
33 #include "g_game.h"
34 
35 #include "r_local.h"
36 #include "s_sound.h"
37 
38 #include "doomstat.h"
39 
40 // Data.
41 #include "sounds.h"
42 
43 // Needs access to LFB.
44 #include "v_video.h"
45 
46 #include "wi_stuff.h"
47 
48 //
49 // Data needed to add patches to full screen intermission pics.
50 // Patches are statistics messages, and animations.
51 // Loads of by-pixel layout and placement, offsets etc.
52 //
53 
54 
55 //
56 // Different vetween registered DOOM (1994) and
57 //  Ultimate DOOM - Final edition (retail, 1995?).
58 // This is supposedly ignored for commercial
59 //  release (aka DOOM II), which had 34 maps
60 //  in one episode. So there.
61 #define NUMEPISODES	4
62 #define NUMMAPS		9
63 
64 
65 // in tics
66 //U #define PAUSELEN		(TICRATE*2)
67 //U #define SCORESTEP		100
68 //U #define ANIMPERIOD		32
69 // pixel distance from "(YOU)" to "PLAYER N"
70 //U #define STARDIST		10
71 //U #define WK 1
72 
73 
74 // GLOBAL LOCATIONS
75 #define WI_TITLEY		2
76 #define WI_SPACINGY    		33
77 
78 // SINGPLE-PLAYER STUFF
79 #define SP_STATSX		50
80 #define SP_STATSY		50
81 
82 #define SP_TIMEX		16
83 #define SP_TIMEY		(SCREENHEIGHT-32)
84 
85 
86 // NET GAME STUFF
87 #define NG_STATSY		50
88 #define NG_STATSX		(32 + SHORT(star->width)/2 + 32*!dofrags)
89 
90 #define NG_SPACINGX    		64
91 
92 
93 // DEATHMATCH STUFF
94 #define DM_MATRIXX		42
95 #define DM_MATRIXY		68
96 
97 #define DM_SPACINGX		40
98 
99 #define DM_TOTALSX		269
100 
101 #define DM_KILLERSX		10
102 #define DM_KILLERSY		100
103 #define DM_VICTIMSX    		5
104 #define DM_VICTIMSY		50
105 
106 
107 
108 
109 typedef enum
110 {
111     ANIM_ALWAYS,
112     ANIM_RANDOM,
113     ANIM_LEVEL
114 
115 } animenum_t;
116 
117 typedef struct
118 {
119     int		x;
120     int		y;
121 
122 } point_t;
123 
124 
125 //
126 // Animation.
127 // There is another anim_t used in p_spec.
128 //
129 typedef struct
130 {
131     animenum_t	type;
132 
133     // period in tics between animations
134     int		period;
135 
136     // number of animation frames
137     int		nanims;
138 
139     // location of animation
140     point_t	loc;
141 
142     // ALWAYS: n/a,
143     // RANDOM: period deviation (<256),
144     // LEVEL: level
145     int		data1;
146 
147     // ALWAYS: n/a,
148     // RANDOM: random base period,
149     // LEVEL: n/a
150     int		data2;
151 
152     // actual graphics for frames of animations
153     patch_t*	p[3];
154 
155     // following must be initialized to zero before use!
156 
157     // next value of bcnt (used in conjunction with period)
158     int		nexttic;
159 
160     // last drawn animation frame
161     int		lastdrawn;
162 
163     // next frame number to animate
164     int		ctr;
165 
166     // used by RANDOM and LEVEL when animating
167     int		state;
168 
169 } anim_t;
170 
171 
172 static point_t lnodes[NUMEPISODES][NUMMAPS] =
173 {
174     // Episode 0 World Map
175     {
176 	{ 185, 164 },	// location of level 0 (CJ)
177 	{ 148, 143 },	// location of level 1 (CJ)
178 	{ 69, 122 },	// location of level 2 (CJ)
179 	{ 209, 102 },	// location of level 3 (CJ)
180 	{ 116, 89 },	// location of level 4 (CJ)
181 	{ 166, 55 },	// location of level 5 (CJ)
182 	{ 71, 56 },	// location of level 6 (CJ)
183 	{ 135, 29 },	// location of level 7 (CJ)
184 	{ 71, 24 }	// location of level 8 (CJ)
185     },
186 
187     // Episode 1 World Map should go here
188     {
189 	{ 254, 25 },	// location of level 0 (CJ)
190 	{ 97, 50 },	// location of level 1 (CJ)
191 	{ 188, 64 },	// location of level 2 (CJ)
192 	{ 128, 78 },	// location of level 3 (CJ)
193 	{ 214, 92 },	// location of level 4 (CJ)
194 	{ 133, 130 },	// location of level 5 (CJ)
195 	{ 208, 136 },	// location of level 6 (CJ)
196 	{ 148, 140 },	// location of level 7 (CJ)
197 	{ 235, 158 }	// location of level 8 (CJ)
198     },
199 
200     // Episode 2 World Map should go here
201     {
202 	{ 156, 168 },	// location of level 0 (CJ)
203 	{ 48, 154 },	// location of level 1 (CJ)
204 	{ 174, 95 },	// location of level 2 (CJ)
205 	{ 265, 75 },	// location of level 3 (CJ)
206 	{ 130, 48 },	// location of level 4 (CJ)
207 	{ 279, 23 },	// location of level 5 (CJ)
208 	{ 198, 48 },	// location of level 6 (CJ)
209 	{ 140, 25 },	// location of level 7 (CJ)
210 	{ 281, 136 }	// location of level 8 (CJ)
211     }
212 
213 };
214 
215 
216 //
217 // Animation locations for episode 0 (1).
218 // Using patches saves a lot of space,
219 //  as they replace 320x200 full screen frames.
220 //
221 
222 #define ANIM(type, period, nanims, x, y, nexttic)            \
223    { (type), (period), (nanims), { (x), (y) }, (nexttic),    \
224      0, { NULL, NULL, NULL }, 0, 0, 0, 0 }
225 
226 
227 static anim_t epsd0animinfo[] =
228 {
229     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 224, 104, 0),
230     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 184, 160, 0),
231     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 112, 136, 0),
232     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 72, 112, 0),
233     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 88, 96, 0),
234     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 48, 0),
235     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 192, 40, 0),
236     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 136, 16, 0),
237     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 80, 16, 0),
238     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 64, 24, 0),
239 };
240 
241 static anim_t epsd1animinfo[] =
242 {
243     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 1),
244     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 2),
245     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 3),
246     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 4),
247     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 5),
248     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 6),
249     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 7),
250     ANIM(ANIM_LEVEL, TICRATE/3, 3, 192, 144, 8),
251     ANIM(ANIM_LEVEL, TICRATE/3, 1, 128, 136, 8),
252 };
253 
254 static anim_t epsd2animinfo[] =
255 {
256     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 168, 0),
257     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 40, 136, 0),
258     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 160, 96, 0),
259     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 104, 80, 0),
260     ANIM(ANIM_ALWAYS, TICRATE/3, 3, 120, 32, 0),
261     ANIM(ANIM_ALWAYS, TICRATE/4, 3, 40, 0, 0),
262 };
263 
264 static int NUMANIMS[NUMEPISODES] =
265 {
266     arrlen(epsd0animinfo),
267     arrlen(epsd1animinfo),
268     arrlen(epsd2animinfo),
269 };
270 
271 static anim_t *anims[NUMEPISODES] =
272 {
273     epsd0animinfo,
274     epsd1animinfo,
275     epsd2animinfo
276 };
277 
278 
279 //
280 // GENERAL DATA
281 //
282 
283 //
284 // Locally used stuff.
285 //
286 
287 // States for single-player
288 #define SP_KILLS		0
289 #define SP_ITEMS		2
290 #define SP_SECRET		4
291 #define SP_FRAGS		6
292 #define SP_TIME			8
293 #define SP_PAR			ST_TIME
294 
295 #define SP_PAUSE		1
296 
297 // in seconds
298 #define SHOWNEXTLOCDELAY	4
299 //#define SHOWLASTLOCDELAY	SHOWNEXTLOCDELAY
300 
301 
302 // used to accelerate or skip a stage
303 static int		acceleratestage;
304 
305 // wbs->pnum
306 static int		me;
307 
308  // specifies current state
309 static stateenum_t	state;
310 
311 // contains information passed into intermission
312 static wbstartstruct_t*	wbs;
313 
314 static wbplayerstruct_t* plrs;  // wbs->plyr[]
315 
316 // used for general timing
317 static int 		cnt;
318 
319 // used for timing of background animation
320 static int 		bcnt;
321 
322 // signals to refresh everything for one frame
323 static int 		firstrefresh;
324 
325 static int		cnt_kills[MAXPLAYERS];
326 static int		cnt_items[MAXPLAYERS];
327 static int		cnt_secret[MAXPLAYERS];
328 static int		cnt_time;
329 static int		cnt_par;
330 static int		cnt_pause;
331 
332 // # of commercial levels
333 static int		NUMCMAPS;
334 
335 
336 //
337 //	GRAPHICS
338 //
339 
340 // You Are Here graphic
341 static patch_t*		yah[3] = { NULL, NULL, NULL };
342 
343 // splat
344 static patch_t*		splat[2] = { NULL, NULL };
345 
346 // %, : graphics
347 static patch_t*		percent;
348 static patch_t*		colon;
349 
350 // 0-9 graphic
351 static patch_t*		num[10];
352 
353 // minus sign
354 static patch_t*		wiminus;
355 
356 // "Finished!" graphics
357 static patch_t*		finished;
358 
359 // "Entering" graphic
360 static patch_t*		entering;
361 
362 // "secret"
363 static patch_t*		sp_secret;
364 
365  // "Kills", "Scrt", "Items", "Frags"
366 static patch_t*		kills;
367 static patch_t*		secret;
368 static patch_t*		items;
369 static patch_t*		frags;
370 
371 // Time sucks.
372 static patch_t*		timepatch;
373 static patch_t*		par;
374 static patch_t*		sucks;
375 
376 // "killers", "victims"
377 static patch_t*		killers;
378 static patch_t*		victims;
379 
380 // "Total", your face, your dead face
381 static patch_t*		total;
382 static patch_t*		star;
383 static patch_t*		bstar;
384 
385 // "red P[1..MAXPLAYERS]"
386 static patch_t*		p[MAXPLAYERS];
387 
388 // "gray P[1..MAXPLAYERS]"
389 static patch_t*		bp[MAXPLAYERS];
390 
391  // Name graphics of each level (centered)
392 static patch_t**	lnames;
393 
394 // Buffer storing the backdrop
395 static patch_t *background;
396 
397 //
398 // CODE
399 //
400 
401 // slam background
402 void WI_slamBackground(void)
403 {
404     V_DrawPatch(0, 0, background);
405 }
406 
407 // The ticker is used to detect keys
408 //  because of timing issues in netgames.
409 boolean WI_Responder(event_t* ev)
410 {
411     return false;
412 }
413 
414 
415 // Draws "<Levelname> Finished!"
416 void WI_drawLF(void)
417 {
418     int y = WI_TITLEY;
419 
420     if (gamemode != commercial || wbs->last < NUMCMAPS)
421     {
422         // draw <LevelName>
423         V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->last]->width))/2,
424                     y, lnames[wbs->last]);
425 
426         // draw "Finished!"
427         y += (5*SHORT(lnames[wbs->last]->height))/4;
428 
429         V_DrawPatch((SCREENWIDTH - SHORT(finished->width)) / 2, y, finished);
430     }
431     else if (wbs->last == NUMCMAPS)
432     {
433         // MAP33 - nothing is displayed!
434     }
435     else if (wbs->last > NUMCMAPS)
436     {
437         // > MAP33.  Doom bombs out here with a Bad V_DrawPatch error.
438         // I'm pretty sure that doom2.exe is just reading into random
439         // bits of memory at this point, but let's try to be accurate
440         // anyway.  This deliberately triggers a V_DrawPatch error.
441 
442         patch_t tmp = { SCREENWIDTH, SCREENHEIGHT, 1, 1,
443                         { 0, 0, 0, 0, 0, 0, 0, 0 } };
444 
445         V_DrawPatch(0, y, &tmp);
446     }
447 }
448 
449 
450 
451 // Draws "Entering <LevelName>"
452 void WI_drawEL(void)
453 {
454     int y = WI_TITLEY;
455 
456     // draw "Entering"
457     V_DrawPatch((SCREENWIDTH - SHORT(entering->width))/2,
458 		y,
459                 entering);
460 
461     // draw level
462     y += (5*SHORT(lnames[wbs->next]->height))/4;
463 
464     V_DrawPatch((SCREENWIDTH - SHORT(lnames[wbs->next]->width))/2,
465 		y,
466                 lnames[wbs->next]);
467 
468 }
469 
470 void
471 WI_drawOnLnode
472 ( int		n,
473   patch_t*	c[] )
474 {
475 
476     int		i;
477     int		left;
478     int		top;
479     int		right;
480     int		bottom;
481     boolean	fits = false;
482 
483     i = 0;
484     do
485     {
486 	left = lnodes[wbs->epsd][n].x - SHORT(c[i]->leftoffset);
487 	top = lnodes[wbs->epsd][n].y - SHORT(c[i]->topoffset);
488 	right = left + SHORT(c[i]->width);
489 	bottom = top + SHORT(c[i]->height);
490 
491 	if (left >= 0
492 	    && right < SCREENWIDTH
493 	    && top >= 0
494 	    && bottom < SCREENHEIGHT)
495 	{
496 	    fits = true;
497 	}
498 	else
499 	{
500 	    i++;
501 	}
502     } while (!fits && i!=2 && c[i] != NULL);
503 
504     if (fits && i<2)
505     {
506 	V_DrawPatch(lnodes[wbs->epsd][n].x,
507                     lnodes[wbs->epsd][n].y,
508 		    c[i]);
509     }
510     else
511     {
512 	// DEBUG
513 	printf("Could not place patch on level %d", n+1);
514     }
515 }
516 
517 
518 
519 void WI_initAnimatedBack(void)
520 {
521     int		i;
522     anim_t*	a;
523 
524     if (gamemode == commercial)
525 	return;
526 
527     if (wbs->epsd > 2)
528 	return;
529 
530     for (i=0;i<NUMANIMS[wbs->epsd];i++)
531     {
532 	a = &anims[wbs->epsd][i];
533 
534 	// init variables
535 	a->ctr = -1;
536 
537 	// specify the next time to draw it
538 	if (a->type == ANIM_ALWAYS)
539 	    a->nexttic = bcnt + 1 + (M_Random()%a->period);
540 	else if (a->type == ANIM_RANDOM)
541 	    a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1);
542 	else if (a->type == ANIM_LEVEL)
543 	    a->nexttic = bcnt + 1;
544     }
545 
546 }
547 
548 void WI_updateAnimatedBack(void)
549 {
550     int		i;
551     anim_t*	a;
552 
553     if (gamemode == commercial)
554 	return;
555 
556     if (wbs->epsd > 2)
557 	return;
558 
559     for (i=0;i<NUMANIMS[wbs->epsd];i++)
560     {
561 	a = &anims[wbs->epsd][i];
562 
563 	if (bcnt == a->nexttic)
564 	{
565 	    switch (a->type)
566 	    {
567 	      case ANIM_ALWAYS:
568 		if (++a->ctr >= a->nanims) a->ctr = 0;
569 		a->nexttic = bcnt + a->period;
570 		break;
571 
572 	      case ANIM_RANDOM:
573 		a->ctr++;
574 		if (a->ctr == a->nanims)
575 		{
576 		    a->ctr = -1;
577 		    a->nexttic = bcnt+a->data2+(M_Random()%a->data1);
578 		}
579 		else a->nexttic = bcnt + a->period;
580 		break;
581 
582 	      case ANIM_LEVEL:
583 		// gawd-awful hack for level anims
584 		if (!(state == StatCount && i == 7)
585 		    && wbs->next == a->data1)
586 		{
587 		    a->ctr++;
588 		    if (a->ctr == a->nanims) a->ctr--;
589 		    a->nexttic = bcnt + a->period;
590 		}
591 		break;
592 	    }
593 	}
594 
595     }
596 
597 }
598 
599 void WI_drawAnimatedBack(void)
600 {
601     int			i;
602     anim_t*		a;
603 
604     if (gamemode == commercial)
605 	return;
606 
607     if (wbs->epsd > 2)
608 	return;
609 
610     for (i=0 ; i<NUMANIMS[wbs->epsd] ; i++)
611     {
612 	a = &anims[wbs->epsd][i];
613 
614 	if (a->ctr >= 0)
615 	    V_DrawPatch(a->loc.x, a->loc.y, a->p[a->ctr]);
616     }
617 
618 }
619 
620 //
621 // Draws a number.
622 // If digits > 0, then use that many digits minimum,
623 //  otherwise only use as many as necessary.
624 // Returns new x position.
625 //
626 
627 int
628 WI_drawNum
629 ( int		x,
630   int		y,
631   int		n,
632   int		digits )
633 {
634 
635     int		fontwidth = SHORT(num[0]->width);
636     int		neg;
637     int		temp;
638 
639     if (digits < 0)
640     {
641 	if (!n)
642 	{
643 	    // make variable-length zeros 1 digit long
644 	    digits = 1;
645 	}
646 	else
647 	{
648 	    // figure out # of digits in #
649 	    digits = 0;
650 	    temp = n;
651 
652 	    while (temp)
653 	    {
654 		temp /= 10;
655 		digits++;
656 	    }
657 	}
658     }
659 
660     neg = n < 0;
661     if (neg)
662 	n = -n;
663 
664     // if non-number, do not draw it
665     if (n == 1994)
666 	return 0;
667 
668     // draw the new number
669     while (digits--)
670     {
671 	x -= fontwidth;
672 	V_DrawPatch(x, y, num[ n % 10 ]);
673 	n /= 10;
674     }
675 
676     // draw a minus sign if necessary
677     if (neg)
678 	V_DrawPatch(x-=8, y, wiminus);
679 
680     return x;
681 
682 }
683 
684 void
685 WI_drawPercent
686 ( int		x,
687   int		y,
688   int		p )
689 {
690     if (p < 0)
691 	return;
692 
693     V_DrawPatch(x, y, percent);
694     WI_drawNum(x, y, p, -1);
695 }
696 
697 
698 
699 //
700 // Display level completion time and par,
701 //  or "sucks" message if overflow.
702 //
703 void
704 WI_drawTime
705 ( int		x,
706   int		y,
707   int		t )
708 {
709 
710     int		div;
711     int		n;
712 
713     if (t<0)
714 	return;
715 
716     if (t <= 61*59)
717     {
718 	div = 1;
719 
720 	do
721 	{
722 	    n = (t / div) % 60;
723 	    x = WI_drawNum(x, y, n, 2) - SHORT(colon->width);
724 	    div *= 60;
725 
726 	    // draw
727 	    if (div==60 || t / div)
728 		V_DrawPatch(x, y, colon);
729 
730 	} while (t / div);
731     }
732     else
733     {
734 	// "sucks"
735 	V_DrawPatch(x - SHORT(sucks->width), y, sucks);
736     }
737 }
738 
739 
740 void WI_End(void)
741 {
742     void WI_unloadData(void);
743     WI_unloadData();
744 }
745 
746 void WI_initNoState(void)
747 {
748     state = NoState;
749     acceleratestage = 0;
750     cnt = 10;
751 }
752 
753 void WI_updateNoState(void) {
754 
755     WI_updateAnimatedBack();
756 
757     if (!--cnt)
758     {
759         // Don't call WI_End yet.  G_WorldDone doesnt immediately
760         // change gamestate, so WI_Drawer is still going to get
761         // run until that happens.  If we do that after WI_End
762         // (which unloads all the graphics), we're in trouble.
763 	//WI_End();
764 	G_WorldDone();
765     }
766 
767 }
768 
769 static boolean		snl_pointeron = false;
770 
771 
772 void WI_initShowNextLoc(void)
773 {
774     state = ShowNextLoc;
775     acceleratestage = 0;
776     cnt = SHOWNEXTLOCDELAY * TICRATE;
777 
778     WI_initAnimatedBack();
779 }
780 
781 void WI_updateShowNextLoc(void)
782 {
783     WI_updateAnimatedBack();
784 
785     if (!--cnt || acceleratestage)
786 	WI_initNoState();
787     else
788 	snl_pointeron = (cnt & 31) < 20;
789 }
790 
791 void WI_drawShowNextLoc(void)
792 {
793 
794     int		i;
795     int		last;
796 
797     WI_slamBackground();
798 
799     // draw animated background
800     WI_drawAnimatedBack();
801 
802     if ( gamemode != commercial)
803     {
804   	if (wbs->epsd > 2)
805 	{
806 	    WI_drawEL();
807 	    return;
808 	}
809 
810 	last = (wbs->last == 8) ? wbs->next - 1 : wbs->last;
811 
812 	// draw a splat on taken cities.
813 	for (i=0 ; i<=last ; i++)
814 	    WI_drawOnLnode(i, splat);
815 
816 	// splat the secret level?
817 	if (wbs->didsecret)
818 	    WI_drawOnLnode(8, splat);
819 
820 	// draw flashing ptr
821 	if (snl_pointeron)
822 	    WI_drawOnLnode(wbs->next, yah);
823     }
824 
825     // draws which level you are entering..
826     if ( (gamemode != commercial)
827 	 || wbs->next != 30)
828 	WI_drawEL();
829 
830 }
831 
832 void WI_drawNoState(void)
833 {
834     snl_pointeron = true;
835     WI_drawShowNextLoc();
836 }
837 
838 int WI_fragSum(int playernum)
839 {
840     int		i;
841     int		frags = 0;
842 
843     for (i=0 ; i<MAXPLAYERS ; i++)
844     {
845 	if (playeringame[i]
846 	    && i!=playernum)
847 	{
848 	    frags += plrs[playernum].frags[i];
849 	}
850     }
851 
852 
853     // JDC hack - negative frags.
854     frags -= plrs[playernum].frags[playernum];
855     // UNUSED if (frags < 0)
856     // 	frags = 0;
857 
858     return frags;
859 }
860 
861 
862 
863 static int		dm_state;
864 static int		dm_frags[MAXPLAYERS][MAXPLAYERS];
865 static int		dm_totals[MAXPLAYERS];
866 
867 
868 
869 void WI_initDeathmatchStats(void)
870 {
871 
872     int		i;
873     int		j;
874 
875     state = StatCount;
876     acceleratestage = 0;
877     dm_state = 1;
878 
879     cnt_pause = TICRATE;
880 
881     for (i=0 ; i<MAXPLAYERS ; i++)
882     {
883 	if (playeringame[i])
884 	{
885 	    for (j=0 ; j<MAXPLAYERS ; j++)
886 		if (playeringame[j])
887 		    dm_frags[i][j] = 0;
888 
889 	    dm_totals[i] = 0;
890 	}
891     }
892 
893     WI_initAnimatedBack();
894 }
895 
896 
897 
898 void WI_updateDeathmatchStats(void)
899 {
900 
901     int		i;
902     int		j;
903 
904     boolean	stillticking;
905 
906     WI_updateAnimatedBack();
907 
908     if (acceleratestage && dm_state != 4)
909     {
910 	acceleratestage = 0;
911 
912 	for (i=0 ; i<MAXPLAYERS ; i++)
913 	{
914 	    if (playeringame[i])
915 	    {
916 		for (j=0 ; j<MAXPLAYERS ; j++)
917 		    if (playeringame[j])
918 			dm_frags[i][j] = plrs[i].frags[j];
919 
920 		dm_totals[i] = WI_fragSum(i);
921 	    }
922 	}
923 
924 
925 	S_StartSound(0, sfx_barexp);
926 	dm_state = 4;
927     }
928 
929 
930     if (dm_state == 2)
931     {
932 	if (!(bcnt&3))
933 	    S_StartSound(0, sfx_pistol);
934 
935 	stillticking = false;
936 
937 	for (i=0 ; i<MAXPLAYERS ; i++)
938 	{
939 	    if (playeringame[i])
940 	    {
941 		for (j=0 ; j<MAXPLAYERS ; j++)
942 		{
943 		    if (playeringame[j]
944 			&& dm_frags[i][j] != plrs[i].frags[j])
945 		    {
946 			if (plrs[i].frags[j] < 0)
947 			    dm_frags[i][j]--;
948 			else
949 			    dm_frags[i][j]++;
950 
951 			if (dm_frags[i][j] > 99)
952 			    dm_frags[i][j] = 99;
953 
954 			if (dm_frags[i][j] < -99)
955 			    dm_frags[i][j] = -99;
956 
957 			stillticking = true;
958 		    }
959 		}
960 		dm_totals[i] = WI_fragSum(i);
961 
962 		if (dm_totals[i] > 99)
963 		    dm_totals[i] = 99;
964 
965 		if (dm_totals[i] < -99)
966 		    dm_totals[i] = -99;
967 	    }
968 
969 	}
970 	if (!stillticking)
971 	{
972 	    S_StartSound(0, sfx_barexp);
973 	    dm_state++;
974 	}
975 
976     }
977     else if (dm_state == 4)
978     {
979 	if (acceleratestage)
980 	{
981 	    S_StartSound(0, sfx_slop);
982 
983 	    if ( gamemode == commercial)
984 		WI_initNoState();
985 	    else
986 		WI_initShowNextLoc();
987 	}
988     }
989     else if (dm_state & 1)
990     {
991 	if (!--cnt_pause)
992 	{
993 	    dm_state++;
994 	    cnt_pause = TICRATE;
995 	}
996     }
997 }
998 
999 
1000 
1001 void WI_drawDeathmatchStats(void)
1002 {
1003 
1004     int		i;
1005     int		j;
1006     int		x;
1007     int		y;
1008     int		w;
1009 
1010     int		lh;	// line height
1011 
1012     lh = WI_SPACINGY;
1013 
1014     WI_slamBackground();
1015 
1016     // draw animated background
1017     WI_drawAnimatedBack();
1018     WI_drawLF();
1019 
1020     // draw stat titles (top line)
1021     V_DrawPatch(DM_TOTALSX-SHORT(total->width)/2,
1022 		DM_MATRIXY-WI_SPACINGY+10,
1023 		total);
1024 
1025     V_DrawPatch(DM_KILLERSX, DM_KILLERSY, killers);
1026     V_DrawPatch(DM_VICTIMSX, DM_VICTIMSY, victims);
1027 
1028     // draw P?
1029     x = DM_MATRIXX + DM_SPACINGX;
1030     y = DM_MATRIXY;
1031 
1032     for (i=0 ; i<MAXPLAYERS ; i++)
1033     {
1034 	if (playeringame[i])
1035 	{
1036 	    V_DrawPatch(x-SHORT(p[i]->width)/2,
1037 			DM_MATRIXY - WI_SPACINGY,
1038 			p[i]);
1039 
1040 	    V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,
1041 			y,
1042 			p[i]);
1043 
1044 	    if (i == me)
1045 	    {
1046 		V_DrawPatch(x-SHORT(p[i]->width)/2,
1047 			    DM_MATRIXY - WI_SPACINGY,
1048 			    bstar);
1049 
1050 		V_DrawPatch(DM_MATRIXX-SHORT(p[i]->width)/2,
1051 			    y,
1052 			    star);
1053 	    }
1054 	}
1055 	else
1056 	{
1057 	    // V_DrawPatch(x-SHORT(bp[i]->width)/2,
1058 	    //   DM_MATRIXY - WI_SPACINGY, bp[i]);
1059 	    // V_DrawPatch(DM_MATRIXX-SHORT(bp[i]->width)/2,
1060 	    //   y, bp[i]);
1061 	}
1062 	x += DM_SPACINGX;
1063 	y += WI_SPACINGY;
1064     }
1065 
1066     // draw stats
1067     y = DM_MATRIXY+10;
1068     w = SHORT(num[0]->width);
1069 
1070     for (i=0 ; i<MAXPLAYERS ; i++)
1071     {
1072 	x = DM_MATRIXX + DM_SPACINGX;
1073 
1074 	if (playeringame[i])
1075 	{
1076 	    for (j=0 ; j<MAXPLAYERS ; j++)
1077 	    {
1078 		if (playeringame[j])
1079 		    WI_drawNum(x+w, y, dm_frags[i][j], 2);
1080 
1081 		x += DM_SPACINGX;
1082 	    }
1083 	    WI_drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);
1084 	}
1085 	y += WI_SPACINGY;
1086     }
1087 }
1088 
1089 static int	cnt_frags[MAXPLAYERS];
1090 static int	dofrags;
1091 static int	ng_state;
1092 
1093 void WI_initNetgameStats(void)
1094 {
1095 
1096     int i;
1097 
1098     state = StatCount;
1099     acceleratestage = 0;
1100     ng_state = 1;
1101 
1102     cnt_pause = TICRATE;
1103 
1104     for (i=0 ; i<MAXPLAYERS ; i++)
1105     {
1106 	if (!playeringame[i])
1107 	    continue;
1108 
1109 	cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;
1110 
1111 	dofrags += WI_fragSum(i);
1112     }
1113 
1114     dofrags = !!dofrags;
1115 
1116     WI_initAnimatedBack();
1117 }
1118 
1119 
1120 
1121 void WI_updateNetgameStats(void)
1122 {
1123 
1124     int		i;
1125     int		fsum;
1126 
1127     boolean	stillticking;
1128 
1129     WI_updateAnimatedBack();
1130 
1131     if (acceleratestage && ng_state != 10)
1132     {
1133 	acceleratestage = 0;
1134 
1135 	for (i=0 ; i<MAXPLAYERS ; i++)
1136 	{
1137 	    if (!playeringame[i])
1138 		continue;
1139 
1140 	    cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
1141 	    cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
1142 	    cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;
1143 
1144 	    if (dofrags)
1145 		cnt_frags[i] = WI_fragSum(i);
1146 	}
1147 	S_StartSound(0, sfx_barexp);
1148 	ng_state = 10;
1149     }
1150 
1151     if (ng_state == 2)
1152     {
1153 	if (!(bcnt&3))
1154 	    S_StartSound(0, sfx_pistol);
1155 
1156 	stillticking = false;
1157 
1158 	for (i=0 ; i<MAXPLAYERS ; i++)
1159 	{
1160 	    if (!playeringame[i])
1161 		continue;
1162 
1163 	    cnt_kills[i] += 2;
1164 
1165 	    if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills)
1166 		cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
1167 	    else
1168 		stillticking = true;
1169 	}
1170 
1171 	if (!stillticking)
1172 	{
1173 	    S_StartSound(0, sfx_barexp);
1174 	    ng_state++;
1175 	}
1176     }
1177     else if (ng_state == 4)
1178     {
1179 	if (!(bcnt&3))
1180 	    S_StartSound(0, sfx_pistol);
1181 
1182 	stillticking = false;
1183 
1184 	for (i=0 ; i<MAXPLAYERS ; i++)
1185 	{
1186 	    if (!playeringame[i])
1187 		continue;
1188 
1189 	    cnt_items[i] += 2;
1190 	    if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems)
1191 		cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
1192 	    else
1193 		stillticking = true;
1194 	}
1195 	if (!stillticking)
1196 	{
1197 	    S_StartSound(0, sfx_barexp);
1198 	    ng_state++;
1199 	}
1200     }
1201     else if (ng_state == 6)
1202     {
1203 	if (!(bcnt&3))
1204 	    S_StartSound(0, sfx_pistol);
1205 
1206 	stillticking = false;
1207 
1208 	for (i=0 ; i<MAXPLAYERS ; i++)
1209 	{
1210 	    if (!playeringame[i])
1211 		continue;
1212 
1213 	    cnt_secret[i] += 2;
1214 
1215 	    if (cnt_secret[i] >= (plrs[i].ssecret * 100) / wbs->maxsecret)
1216 		cnt_secret[i] = (plrs[i].ssecret * 100) / wbs->maxsecret;
1217 	    else
1218 		stillticking = true;
1219 	}
1220 
1221 	if (!stillticking)
1222 	{
1223 	    S_StartSound(0, sfx_barexp);
1224 	    ng_state += 1 + 2*!dofrags;
1225 	}
1226     }
1227     else if (ng_state == 8)
1228     {
1229 	if (!(bcnt&3))
1230 	    S_StartSound(0, sfx_pistol);
1231 
1232 	stillticking = false;
1233 
1234 	for (i=0 ; i<MAXPLAYERS ; i++)
1235 	{
1236 	    if (!playeringame[i])
1237 		continue;
1238 
1239 	    cnt_frags[i] += 1;
1240 
1241 	    if (cnt_frags[i] >= (fsum = WI_fragSum(i)))
1242 		cnt_frags[i] = fsum;
1243 	    else
1244 		stillticking = true;
1245 	}
1246 
1247 	if (!stillticking)
1248 	{
1249 	    S_StartSound(0, sfx_pldeth);
1250 	    ng_state++;
1251 	}
1252     }
1253     else if (ng_state == 10)
1254     {
1255 	if (acceleratestage)
1256 	{
1257 	    S_StartSound(0, sfx_sgcock);
1258 	    if ( gamemode == commercial )
1259 		WI_initNoState();
1260 	    else
1261 		WI_initShowNextLoc();
1262 	}
1263     }
1264     else if (ng_state & 1)
1265     {
1266 	if (!--cnt_pause)
1267 	{
1268 	    ng_state++;
1269 	    cnt_pause = TICRATE;
1270 	}
1271     }
1272 }
1273 
1274 
1275 
1276 void WI_drawNetgameStats(void)
1277 {
1278     int		i;
1279     int		x;
1280     int		y;
1281     int		pwidth = SHORT(percent->width);
1282 
1283     WI_slamBackground();
1284 
1285     // draw animated background
1286     WI_drawAnimatedBack();
1287 
1288     WI_drawLF();
1289 
1290     // draw stat titles (top line)
1291     V_DrawPatch(NG_STATSX+NG_SPACINGX-SHORT(kills->width),
1292 		NG_STATSY, kills);
1293 
1294     V_DrawPatch(NG_STATSX+2*NG_SPACINGX-SHORT(items->width),
1295 		NG_STATSY, items);
1296 
1297     V_DrawPatch(NG_STATSX+3*NG_SPACINGX-SHORT(secret->width),
1298 		NG_STATSY, secret);
1299 
1300     if (dofrags)
1301 	V_DrawPatch(NG_STATSX+4*NG_SPACINGX-SHORT(frags->width),
1302 		    NG_STATSY, frags);
1303 
1304     // draw stats
1305     y = NG_STATSY + SHORT(kills->height);
1306 
1307     for (i=0 ; i<MAXPLAYERS ; i++)
1308     {
1309 	if (!playeringame[i])
1310 	    continue;
1311 
1312 	x = NG_STATSX;
1313 	V_DrawPatch(x-SHORT(p[i]->width), y, p[i]);
1314 
1315 	if (i == me)
1316 	    V_DrawPatch(x-SHORT(p[i]->width), y, star);
1317 
1318 	x += NG_SPACINGX;
1319 	WI_drawPercent(x-pwidth, y+10, cnt_kills[i]);	x += NG_SPACINGX;
1320 	WI_drawPercent(x-pwidth, y+10, cnt_items[i]);	x += NG_SPACINGX;
1321 	WI_drawPercent(x-pwidth, y+10, cnt_secret[i]);	x += NG_SPACINGX;
1322 
1323 	if (dofrags)
1324 	    WI_drawNum(x, y+10, cnt_frags[i], -1);
1325 
1326 	y += WI_SPACINGY;
1327     }
1328 
1329 }
1330 
1331 static int	sp_state;
1332 
1333 void WI_initStats(void)
1334 {
1335     state = StatCount;
1336     acceleratestage = 0;
1337     sp_state = 1;
1338     cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
1339     cnt_time = cnt_par = -1;
1340     cnt_pause = TICRATE;
1341 
1342     WI_initAnimatedBack();
1343 }
1344 
1345 void WI_updateStats(void)
1346 {
1347 
1348     WI_updateAnimatedBack();
1349 
1350     if (acceleratestage && sp_state != 10)
1351     {
1352 	acceleratestage = 0;
1353 	cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
1354 	cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
1355 	cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;
1356 	cnt_time = plrs[me].stime / TICRATE;
1357 	cnt_par = wbs->partime / TICRATE;
1358 	S_StartSound(0, sfx_barexp);
1359 	sp_state = 10;
1360     }
1361 
1362     if (sp_state == 2)
1363     {
1364 	cnt_kills[0] += 2;
1365 
1366 	if (!(bcnt&3))
1367 	    S_StartSound(0, sfx_pistol);
1368 
1369 	if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills)
1370 	{
1371 	    cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
1372 	    S_StartSound(0, sfx_barexp);
1373 	    sp_state++;
1374 	}
1375     }
1376     else if (sp_state == 4)
1377     {
1378 	cnt_items[0] += 2;
1379 
1380 	if (!(bcnt&3))
1381 	    S_StartSound(0, sfx_pistol);
1382 
1383 	if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems)
1384 	{
1385 	    cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
1386 	    S_StartSound(0, sfx_barexp);
1387 	    sp_state++;
1388 	}
1389     }
1390     else if (sp_state == 6)
1391     {
1392 	cnt_secret[0] += 2;
1393 
1394 	if (!(bcnt&3))
1395 	    S_StartSound(0, sfx_pistol);
1396 
1397 	if (cnt_secret[0] >= (plrs[me].ssecret * 100) / wbs->maxsecret)
1398 	{
1399 	    cnt_secret[0] = (plrs[me].ssecret * 100) / wbs->maxsecret;
1400 	    S_StartSound(0, sfx_barexp);
1401 	    sp_state++;
1402 	}
1403     }
1404 
1405     else if (sp_state == 8)
1406     {
1407 	if (!(bcnt&3))
1408 	    S_StartSound(0, sfx_pistol);
1409 
1410 	cnt_time += 3;
1411 
1412 	if (cnt_time >= plrs[me].stime / TICRATE)
1413 	    cnt_time = plrs[me].stime / TICRATE;
1414 
1415 	cnt_par += 3;
1416 
1417 	if (cnt_par >= wbs->partime / TICRATE)
1418 	{
1419 	    cnt_par = wbs->partime / TICRATE;
1420 
1421 	    if (cnt_time >= plrs[me].stime / TICRATE)
1422 	    {
1423 		S_StartSound(0, sfx_barexp);
1424 		sp_state++;
1425 	    }
1426 	}
1427     }
1428     else if (sp_state == 10)
1429     {
1430 	if (acceleratestage)
1431 	{
1432 	    S_StartSound(0, sfx_sgcock);
1433 
1434 	    if (gamemode == commercial)
1435 		WI_initNoState();
1436 	    else
1437 		WI_initShowNextLoc();
1438 	}
1439     }
1440     else if (sp_state & 1)
1441     {
1442 	if (!--cnt_pause)
1443 	{
1444 	    sp_state++;
1445 	    cnt_pause = TICRATE;
1446 	}
1447     }
1448 
1449 }
1450 
1451 void WI_drawStats(void)
1452 {
1453     // line height
1454     int lh;
1455 
1456     lh = (3*SHORT(num[0]->height))/2;
1457 
1458     WI_slamBackground();
1459 
1460     // draw animated background
1461     WI_drawAnimatedBack();
1462 
1463     WI_drawLF();
1464 
1465     V_DrawPatch(SP_STATSX, SP_STATSY, kills);
1466     WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY, cnt_kills[0]);
1467 
1468     V_DrawPatch(SP_STATSX, SP_STATSY+lh, items);
1469     WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+lh, cnt_items[0]);
1470 
1471     V_DrawPatch(SP_STATSX, SP_STATSY+2*lh, sp_secret);
1472     WI_drawPercent(SCREENWIDTH - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]);
1473 
1474     V_DrawPatch(SP_TIMEX, SP_TIMEY, timepatch);
1475     WI_drawTime(SCREENWIDTH/2 - SP_TIMEX, SP_TIMEY, cnt_time);
1476 
1477     if (wbs->epsd < 3)
1478     {
1479 	V_DrawPatch(SCREENWIDTH/2 + SP_TIMEX, SP_TIMEY, par);
1480 	WI_drawTime(SCREENWIDTH - SP_TIMEX, SP_TIMEY, cnt_par);
1481     }
1482 
1483 }
1484 
1485 void WI_checkForAccelerate(void)
1486 {
1487     int   i;
1488     player_t  *player;
1489 
1490     // check for button presses to skip delays
1491     for (i=0, player = players ; i<MAXPLAYERS ; i++, player++)
1492     {
1493 	if (playeringame[i])
1494 	{
1495 	    if (player->cmd.buttons & BT_ATTACK)
1496 	    {
1497 		if (!player->attackdown)
1498 		    acceleratestage = 1;
1499 		player->attackdown = true;
1500 	    }
1501 	    else
1502 		player->attackdown = false;
1503 	    if (player->cmd.buttons & BT_USE)
1504 	    {
1505 		if (!player->usedown)
1506 		    acceleratestage = 1;
1507 		player->usedown = true;
1508 	    }
1509 	    else
1510 		player->usedown = false;
1511 	}
1512     }
1513 }
1514 
1515 
1516 
1517 // Updates stuff each tick
1518 void WI_Ticker(void)
1519 {
1520     // counter for general background animation
1521     bcnt++;
1522 
1523     if (bcnt == 1)
1524     {
1525 	// intermission music
1526   	if ( gamemode == commercial )
1527 	  S_ChangeMusic(mus_dm2int, true);
1528 	else
1529 	  S_ChangeMusic(mus_inter, true);
1530     }
1531 
1532     WI_checkForAccelerate();
1533 
1534     switch (state)
1535     {
1536       case StatCount:
1537 	if (deathmatch) WI_updateDeathmatchStats();
1538 	else if (netgame) WI_updateNetgameStats();
1539 	else WI_updateStats();
1540 	break;
1541 
1542       case ShowNextLoc:
1543 	WI_updateShowNextLoc();
1544 	break;
1545 
1546       case NoState:
1547 	WI_updateNoState();
1548 	break;
1549     }
1550 
1551 }
1552 
1553 typedef void (*load_callback_t)(char *lumpname, patch_t **variable);
1554 
1555 // Common load/unload function.  Iterates over all the graphics
1556 // lumps to be loaded/unloaded into memory.
1557 
1558 static void WI_loadUnloadData(load_callback_t callback)
1559 {
1560     int i, j;
1561     char name[9];
1562     anim_t *a;
1563 
1564     if (gamemode == commercial)
1565     {
1566 	for (i=0 ; i<NUMCMAPS ; i++)
1567 	{
1568 	    DEH_snprintf(name, 9, "CWILV%2.2d", i);
1569             callback(name, &lnames[i]);
1570 	}
1571     }
1572     else
1573     {
1574 	for (i=0 ; i<NUMMAPS ; i++)
1575 	{
1576 	    DEH_snprintf(name, 9, "WILV%d%d", wbs->epsd, i);
1577             callback(name, &lnames[i]);
1578 	}
1579 
1580 	// you are here
1581         callback(DEH_String("WIURH0"), &yah[0]);
1582 
1583 	// you are here (alt.)
1584         callback(DEH_String("WIURH1"), &yah[1]);
1585 
1586 	// splat
1587         callback(DEH_String("WISPLAT"), &splat[0]);
1588 
1589 	if (wbs->epsd < 3)
1590 	{
1591 	    for (j=0;j<NUMANIMS[wbs->epsd];j++)
1592 	    {
1593 		a = &anims[wbs->epsd][j];
1594 		for (i=0;i<a->nanims;i++)
1595 		{
1596 		    // MONDO HACK!
1597 		    if (wbs->epsd != 1 || j != 8)
1598 		    {
1599 			// animations
1600 			DEH_snprintf(name, 9, "WIA%d%.2d%.2d", wbs->epsd, j, i);
1601                         callback(name, &a->p[i]);
1602 		    }
1603 		    else
1604 		    {
1605 			// HACK ALERT!
1606 			a->p[i] = anims[1][4].p[i];
1607 		    }
1608 		}
1609 	    }
1610 	}
1611     }
1612 
1613     // More hacks on minus sign.
1614     callback(DEH_String("WIMINUS"), &wiminus);
1615 
1616     for (i=0;i<10;i++)
1617     {
1618 	 // numbers 0-9
1619 	DEH_snprintf(name, 9, "WINUM%d", i);
1620         callback(name, &num[i]);
1621     }
1622 
1623     // percent sign
1624     callback(DEH_String("WIPCNT"), &percent);
1625 
1626     // "finished"
1627     callback(DEH_String("WIF"), &finished);
1628 
1629     // "entering"
1630     callback(DEH_String("WIENTER"), &entering);
1631 
1632     // "kills"
1633     callback(DEH_String("WIOSTK"), &kills);
1634 
1635     // "scrt"
1636     callback(DEH_String("WIOSTS"), &secret);
1637 
1638      // "secret"
1639     callback(DEH_String("WISCRT2"), &sp_secret);
1640 
1641     // french wad uses WIOBJ (?)
1642     if (W_CheckNumForName(DEH_String("WIOBJ")) >= 0)
1643     {
1644     	// "items"
1645     	if (netgame && !deathmatch)
1646             callback(DEH_String("WIOBJ"), &items);
1647     	else
1648             callback(DEH_String("WIOSTI"), &items);
1649     } else {
1650         callback(DEH_String("WIOSTI"), &items);
1651     }
1652 
1653     // "frgs"
1654     callback(DEH_String("WIFRGS"), &frags);
1655 
1656     // ":"
1657     callback(DEH_String("WICOLON"), &colon);
1658 
1659     // "time"
1660     callback(DEH_String("WITIME"), &timepatch);
1661 
1662     // "sucks"
1663     callback(DEH_String("WISUCKS"), &sucks);
1664 
1665     // "par"
1666     callback(DEH_String("WIPAR"), &par);
1667 
1668     // "killers" (vertical)
1669     callback(DEH_String("WIKILRS"), &killers);
1670 
1671     // "victims" (horiz)
1672     callback(DEH_String("WIVCTMS"), &victims);
1673 
1674     // "total"
1675     callback(DEH_String("WIMSTT"), &total);
1676 
1677     for (i=0 ; i<MAXPLAYERS ; i++)
1678     {
1679 	// "1,2,3,4"
1680 	DEH_snprintf(name, 9, "STPB%d", i);
1681         callback(name, &p[i]);
1682 
1683 	// "1,2,3,4"
1684 	DEH_snprintf(name, 9, "WIBP%d", i+1);
1685         callback(name, &bp[i]);
1686     }
1687 
1688     // Background image
1689 
1690     if (gamemode == commercial)
1691     {
1692 	M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name));
1693         name[8] = '\0';
1694     }
1695     else if (gamemode == retail && wbs->epsd == 3)
1696     {
1697 	M_StringCopy(name, DEH_String("INTERPIC"), sizeof(name));
1698         name[8] = '\0';
1699     }
1700     else
1701     {
1702 	DEH_snprintf(name, 9, "WIMAP%d", wbs->epsd);
1703     }
1704 
1705     // Draw backdrop and save to a temporary buffer
1706 
1707     callback(name, &background);
1708 }
1709 
1710 static void WI_loadCallback(char *name, patch_t **variable)
1711 {
1712     *variable = W_CacheLumpName(name, PU_STATIC);
1713 }
1714 
1715 void WI_loadData(void)
1716 {
1717     if (gamemode == commercial)
1718     {
1719 	NUMCMAPS = 32;
1720 	lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMCMAPS,
1721 				       PU_STATIC, NULL);
1722     }
1723     else
1724     {
1725 	lnames = (patch_t **) Z_Malloc(sizeof(patch_t*) * NUMMAPS,
1726 				       PU_STATIC, NULL);
1727     }
1728 
1729     WI_loadUnloadData(WI_loadCallback);
1730 
1731     // These two graphics are special cased because we're sharing
1732     // them with the status bar code
1733 
1734     // your face
1735     star = W_CacheLumpName(DEH_String("STFST01"), PU_STATIC);
1736 
1737     // dead face
1738     bstar = W_CacheLumpName(DEH_String("STFDEAD0"), PU_STATIC);
1739 }
1740 
1741 static void WI_unloadCallback(char *name, patch_t **variable)
1742 {
1743     W_ReleaseLumpName(name);
1744     *variable = NULL;
1745 }
1746 
1747 void WI_unloadData(void)
1748 {
1749     WI_loadUnloadData(WI_unloadCallback);
1750 
1751     // We do not free these lumps as they are shared with the status
1752     // bar code.
1753 
1754     // W_ReleaseLumpName("STFST01");
1755     // W_ReleaseLumpName("STFDEAD0");
1756 }
1757 
1758 void WI_Drawer (void)
1759 {
1760     switch (state)
1761     {
1762       case StatCount:
1763 	if (deathmatch)
1764 	    WI_drawDeathmatchStats();
1765 	else if (netgame)
1766 	    WI_drawNetgameStats();
1767 	else
1768 	    WI_drawStats();
1769 	break;
1770 
1771       case ShowNextLoc:
1772 	WI_drawShowNextLoc();
1773 	break;
1774 
1775       case NoState:
1776 	WI_drawNoState();
1777 	break;
1778     }
1779 }
1780 
1781 
1782 void WI_initVariables(wbstartstruct_t* wbstartstruct)
1783 {
1784 
1785     wbs = wbstartstruct;
1786 
1787 #ifdef RANGECHECKING
1788     if (gamemode != commercial)
1789     {
1790       if ( gamemode == retail )
1791 	RNGCHECK(wbs->epsd, 0, 3);
1792       else
1793 	RNGCHECK(wbs->epsd, 0, 2);
1794     }
1795     else
1796     {
1797 	RNGCHECK(wbs->last, 0, 8);
1798 	RNGCHECK(wbs->next, 0, 8);
1799     }
1800     RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
1801     RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
1802 #endif
1803 
1804     acceleratestage = 0;
1805     cnt = bcnt = 0;
1806     firstrefresh = 1;
1807     me = wbs->pnum;
1808     plrs = wbs->plyr;
1809 
1810     if (!wbs->maxkills)
1811 	wbs->maxkills = 1;
1812 
1813     if (!wbs->maxitems)
1814 	wbs->maxitems = 1;
1815 
1816     if (!wbs->maxsecret)
1817 	wbs->maxsecret = 1;
1818 
1819     if ( gamemode != retail )
1820       if (wbs->epsd > 2)
1821 	wbs->epsd -= 3;
1822 }
1823 
1824 void WI_Start(wbstartstruct_t* wbstartstruct)
1825 {
1826     WI_initVariables(wbstartstruct);
1827     WI_loadData();
1828 
1829     if (deathmatch)
1830 	WI_initDeathmatchStats();
1831     else if (netgame)
1832 	WI_initNetgameStats();
1833     else
1834 	WI_initStats();
1835 }
1836 #endif
1837