1//**************************************************************************
2//**
3//**    ##   ##    ##    ##   ##   ####     ####   ###     ###
4//**    ##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5//**     ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6//**     ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7//**      ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8//**       #    ##    ##    #      ####     ####   ##       ##
9//**
10//**    $Id: IntermissionScreen.vc 4082 2009-10-22 11:48:22Z dj_jl $
11//**
12//**    Copyright (C) 1999-2006 Jānis Legzdiņš
13//**
14//**    This program is free software; you can redistribute it and/or
15//**  modify it under the terms of the GNU General Public License
16//**  as published by the Free Software Foundation; either version 2
17//**  of the License, or (at your option) any later version.
18//**
19//**    This program is distributed in the hope that it will be useful,
20//**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21//**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22//**  GNU General Public License for more details.
23//**
24//**************************************************************************
25
26class IntermissionScreen : Widget;
27
28//	Intermission states
29enum
30{
31	IMS_Stats,
32	IMS_NextLoc,
33	IMS_NoState,
34	IMS_Text,
35	IMS_Done
36};
37
38ClientGameBase ClGame;
39
40// GLOBAL LOCATIONS
41const int WI_SPACINGY	= 33;
42
43const int WI_TITLEY		= 12;
44
45// SINGPLE-PLAYER STUFF
46const int SP_STATSX		= 210;
47const int SP_STATSY		= 190;
48
49const int SP_TIMEX		= 176;
50const int SP_TIMEY		= 308;
51
52const int SP_PARX		= 376;
53const int SP_PARY		= 308;
54
55// NET GAME STUFF
56const int NG_STATSY		= 210;
57const int NG_STATSX		= (172 + 32 / 2);
58
59const int NG_SPACINGX    	= 64;
60
61// DEATHMATCH STUFF
62const int DM_MATRIXX		= 202;
63const int DM_MATRIXY		= 208;
64
65const int DM_TOTALSX		= 329;
66const int DM_DEATHSX		= 429;
67
68const int DM_SPACINGX		= 40;
69
70// in seconds
71const float SHOWNEXTLOCDELAY	= 4.0;
72
73//  Different between registered DOOM (1994) and Ultimate DOOM - Final
74// edition (retail, 1995?).
75const int NUMEPISODES		= 3;
76const int NUMMAPS			= 9;
77
78const float TEXTSPEED	= 0.1;
79const float TEXTWAIT	= 7.0;
80
81int LeaveTitlePic;
82int EnterTitlePic;
83
84int interstate;
85
86// used for timing of background animation
87float btime;
88
89int sp_state;
90int ng_state;
91int dm_state;
92
93int				TotalKills;
94int				TotalItems;
95int				TotalSecret;
96
97int cnt_kills[MAXPLAYERS];
98int cnt_items[MAXPLAYERS];
99int cnt_secret[MAXPLAYERS];
100int cnt_time;
101float cnt_pause;
102int cnt_frags[MAXPLAYERS];
103
104int dm_Frags[MAXPLAYERS];
105int dm_Deaths[MAXPLAYERS];
106
107// Face background, your face, your dead face
108int p[MAXPLAYERS];
109
110int star;
111int star_width;
112
113int bstar;
114
115// used for general timing
116float cnt;
117
118#ifndef DOOM2
119
120int im_episode;
121int im_map;
122int im_nextepisode;
123int im_nextmap;
124
125#endif
126
127IntermissionBackground		Background;
128
129//==========================================================================
130//
131//  Start
132//
133//==========================================================================
134
135void Start()
136{
137	ClGame.skipintermission = false;
138	cnt = 0.0;
139	btime = 0.0;
140
141	if (!Background)
142	{
143		Background = NewChild(IntermissionBackground);
144		Background.ScaleX = itof(Width) / itof(Background.Width);
145		Background.ScaleY = itof(Height) / itof(Background.Height);
146	}
147
148#ifndef DOOM2
149	FindEpisodeAndMap(ClGame.im.LeaveMap, &im_episode, &im_map);
150	FindEpisodeAndMap(ClGame.im.EnterMap, &im_nextepisode, &im_nextmap);
151#endif
152
153	IM_InitAnimatedBack();
154
155	TotalKills = ClGame.cl.Level.TotalKills;
156	TotalItems = ClGame.cl.Level.TotalItems;
157	TotalSecret = ClGame.cl.Level.TotalSecret;
158
159	if (!TotalKills)
160		TotalKills = 1;
161
162	if (!TotalItems)
163		TotalItems = 1;
164
165	if (!TotalSecret)
166		TotalSecret = 1;
167
168	if (ClGame.deathmatch)
169		IM_InitDeathmatchStats();
170	else if (ClGame.maxclients > 1)
171		IM_InitCooperativeStats();
172	else
173		IM_InitSingleStats();
174
175	if (interstate == IMS_Stats)
176	{
177		IM_LoadData();
178
179		// intermission music
180		if (ClGame.im.InterMusic)
181		{
182			CmdBuf_AddText(va("music loop %n\n", ClGame.im.InterMusic));
183		}
184		else
185		{
186#ifdef FIXME
187#ifdef DOOM2
188			S_StartSong("D_DM2INT", P_GetCDIntermissionTrack(), true);
189#else
190			S_StartSong("D_INTER", P_GetCDIntermissionTrack(), true);
191#endif
192#else
193#ifdef DOOM2
194			CmdBuf_AddText("music loop D_DM2INT\n");
195#else
196			CmdBuf_AddText("music loop D_INTER\n");
197#endif
198#endif
199		}
200	}
201	else
202	{
203		IM_InitText();
204	}
205}
206
207#ifndef DOOM2
208//==========================================================================
209//
210//  FindEpisodeAndMap
211//
212//==========================================================================
213
214void FindEpisodeAndMap(name Name, int *epi, int *map)
215{
216	int e;
217	int m;
218
219	for (e = 1; e <= 3; e++)
220	{
221		for (m = 1; m <= 9; m++)
222		{
223			if (Name == StrToName(va("e%dm%d", e, m)))
224			{
225				*epi = e;
226				*map = m;
227				return;
228			}
229		}
230	}
231	*epi = 4;
232	*map = 1;
233}
234#endif
235
236//==========================================================================
237//
238//	Tick
239//
240//  Updates stuff each frame
241//
242//==========================================================================
243
244void Tick(float DeltaTime)
245{
246	// counter for general background animation
247	btime += DeltaTime;
248
249	switch (interstate)
250	{
251	case IMS_Stats:
252		if (ClGame.deathmatch)
253			IM_UpdateDeathmatchStats(DeltaTime);
254		else if (ClGame.maxclients > 1)
255			IM_UpdateCooperativeStats(DeltaTime);
256		else
257			IM_UpdateSingleStats(DeltaTime);
258		break;
259
260#ifndef DOOM2
261	case IMS_NextLoc:
262		IM_UpdateNextLoc(DeltaTime);
263		break;
264#endif
265
266	case IMS_NoState:
267		IM_UpdateNoState(DeltaTime);
268		break;
269
270	case IMS_Text:
271		IM_UpdateText(DeltaTime);
272		break;
273
274	case IMS_Done:
275		break;
276	}
277}
278
279//==========================================================================
280//
281//  OnPostDraw
282//
283//==========================================================================
284
285void OnPostDraw()
286{
287	SetFont('bigfont');
288
289	switch (interstate)
290	{
291	case IMS_Stats:
292		if (ClGame.deathmatch)
293		{
294			IM_DrawDeathmatchStats();
295		}
296		else if (ClGame.maxclients > 1)
297		{
298			IM_DrawCooperativeStats();
299		}
300		else
301		{
302			IM_DrawSingleStats();
303		}
304		break;
305
306#ifndef DOOM2
307	case IMS_NextLoc:
308		IM_DrawNextLoc();
309		break;
310#endif
311
312	case IMS_NoState:
313		IM_DrawNoState();
314		break;
315
316	case IMS_Text:
317		IM_DrawText();
318		break;
319
320	case IMS_Done:
321		break;
322	}
323}
324
325//**************************************************************************
326//**
327//**    Animated background
328//**
329//**************************************************************************
330
331//==========================================================================
332//
333//  IM_InitAnimatedBack
334//
335//==========================================================================
336
337void IM_InitAnimatedBack()
338{
339	name BgPic = interstate ? ClGame.im.ExitPic : ClGame.im.EnterPic;
340	if (!BgPic)
341	{
342#ifdef DOOM2
343		BgPic = 'interpic';
344#else
345		if (im_episode > 3)
346		{
347			BgPic = 'interpic';
348		}
349		else
350		{
351			BgPic = StrToName(va("$in_epi%d", im_episode));
352		}
353#endif
354	}
355	Background.Init(BgPic, interstate ?
356		IntermissionBackground::STATE_NextLoc :
357		IntermissionBackground::STATE_Default, ClientGameShared(ClGame));
358}
359
360//==========================================================================
361//
362//  IM_LoadData
363//
364//==========================================================================
365
366void IM_LoadData()
367{
368	picinfo_t info;
369	int i;
370
371	if (ClGame.im.LeaveTitlePatch)
372	{
373		LeaveTitlePic = R_RegisterPic(ClGame.im.LeaveTitlePatch);
374	}
375	if (ClGame.im.EnterTitlePatch)
376	{
377		EnterTitlePic = R_RegisterPic(ClGame.im.EnterTitlePatch);
378	}
379
380	if (ClGame.maxclients > 1 || ClGame.deathmatch)
381	{
382		// face backgrounds
383//      for (i = 0; i < MAXPLAYERS; i++)
384		for (i = 0; i < 4; i++)
385		{
386			p[i] = R_RegisterPic(StrToName(va("stpb%i", i)));
387		}
388
389		// your face
390		star = R_RegisterPic('stfst01');
391		R_GetPicInfo(star, &info);
392		star_width = info.width;
393
394		// dead face
395		bstar = R_RegisterPic('stfdead0');
396	}
397}
398
399//==========================================================================
400//
401//  IM_UnloadData
402//
403//==========================================================================
404
405void IM_UnloadData()
406{
407}
408
409//==========================================================================
410//
411//  IM_DrawLF
412//
413//  Draws "<Levelname> Finished!"
414//
415//==========================================================================
416
417void IM_DrawLF()
418{
419	picinfo_t info;
420	int y = WI_TITLEY;
421
422	// draw <LevelName>
423	if (ClGame.im.LeaveTitlePatch)
424	{
425		R_GetPicInfo(LeaveTitlePic, &info);
426		DrawPic(320 - info.width / 2, y, LeaveTitlePic);
427		y += info.height + TextHeight("DUMMY") / 4;
428	}
429	else
430	{
431		SetTextAlign(hcentre, vtop);
432		DrawText(320, y, ClGame.im.LeaveName);
433		y += (5 * TextHeight("DUMMY")) / 4;
434	}
435
436	// draw "Finished!"
437	SetTextAlign(hcentre, vtop);
438	DrawText(320, y, "finished");
439}
440
441//==========================================================================
442//
443//  IM_DrawEL
444//
445//  Draws "Entering <LevelName>"
446//
447//==========================================================================
448
449void IM_DrawEL()
450{
451	picinfo_t info;
452	int y = WI_TITLEY;
453
454	// draw "Entering"
455	SetTextAlign(hcentre, vtop);
456	DrawText(320, y, "entering");
457
458	// draw level
459	y += (5 * TextHeight("DUMMY")) / 4;
460	if (ClGame.im.EnterTitlePatch)
461	{
462		R_GetPicInfo(EnterTitlePic, &info);
463		DrawPic(320 - info.width / 2, y, EnterTitlePic);
464	}
465	else
466	{
467		DrawText(320, y, ClGame.im.EnterName);
468	}
469}
470
471//==========================================================================
472//
473//  IM_DrawPercent
474//
475//==========================================================================
476
477void IM_DrawPercent(int x, int y, int p)
478{
479	if (p < 0)
480		return;
481
482	SetTextAlign(hright, vtop);
483	DrawText(x, y, va("%d%%", p));
484}
485
486//==========================================================================
487//
488//  IM_DrawTime
489//
490//  Display level completion time and par, or "sucks" message if overflow.
491//
492//==========================================================================
493
494void IM_DrawTime(int x, int y, int t, string BaseText, bool sucks)
495{
496	if (t < 0)
497		return;
498
499	SetTextAlign(hleft, vtop);
500
501	string Text;
502	if (!sucks || !ClGame.cl.Level.SuckTime || t < ClGame.cl.Level.SuckTime * 60 * 60)
503	{
504		Text = BaseText;
505		if (t >= 60 * 60)
506		{
507			Text = strcat(Text, va("%d:", t / (60 * 60)));
508			if ((t / 60) % 60 < 10)
509				Text = strcat(Text, va("0%d", (t / 60) % 60));
510			else
511				Text = strcat(Text, va("%d", (t / 60) % 60));
512		}
513		else
514			Text = strcat(Text, va("%d", t / 60));
515		if (t % 60 < 10)
516			Text = strcat(Text, va(":0%d", t % 60));
517		else
518			Text = strcat(Text, va(":%d", t % 60));
519	}
520	else
521	{
522		// "sucks"
523		Text = strcat(BaseText, "sucks");
524	}
525	DrawText(x, y, Text);
526}
527
528//==========================================================================
529//
530//  IM_InitSingleStats
531//
532//==========================================================================
533
534void IM_InitSingleStats()
535{
536	interstate = IMS_Stats;
537	ClGame.skipintermission = 0;
538	sp_state = 1;
539	cnt_kills[0] = -1;
540	cnt_items[0] = -1;
541	cnt_secret[0] = -1;
542	cnt_time = -1;
543	cnt_pause = 1.0;
544	if (ClGame.cl.Level.bNoIntermission)
545	{
546		interstate = IMS_NoState;
547	}
548}
549
550//==========================================================================
551//
552//  IM_DrawSingleStats
553//
554//==========================================================================
555
556void IM_DrawSingleStats()
557{
558	// line height
559	int lh;
560
561	lh = (3 * TextHeight("DUMMY")) / 2;
562
563	IM_DrawLF();
564
565	SetTextAlign(hleft, vtop);
566
567	DrawText(SP_STATSX, SP_STATSY, "kills");
568	DrawText(SP_STATSX, SP_STATSY + lh, "items");
569	DrawText(SP_STATSX, SP_STATSY + 2 * lh, "scrt");
570
571	IM_DrawPercent(640 - SP_STATSX, SP_STATSY, cnt_kills[0]);
572	IM_DrawPercent(640 - SP_STATSX, SP_STATSY + lh, cnt_items[0]);
573	IM_DrawPercent(640 - SP_STATSX, SP_STATSY + 2 * lh, cnt_secret[0]);
574	IM_DrawTime(SP_TIMEX, SP_TIMEY, cnt_time, "time: ", true);
575	if (cnt_time >= 0 && ClGame.cl.Level.ParTime)
576		IM_DrawTime(SP_PARX, SP_PARY, ClGame.cl.Level.ParTime, "par: ", false);
577}
578
579//==========================================================================
580//
581//  IM_UpdateSingleStats
582//
583//==========================================================================
584
585void IM_UpdateSingleStats(float DeltaTime)
586{
587	if (ClGame.skipintermission && sp_state != 10)
588	{
589		ClGame.skipintermission = 0;
590		cnt_kills[0] = (ClGame.cl.KillCount * 100) / TotalKills;
591		cnt_items[0] = (ClGame.cl.ItemCount * 100) / TotalItems;
592		cnt_secret[0] = (ClGame.cl.SecretCount * 100) / TotalSecret;
593		cnt_time = ftoi(ClGame.GLevel.LevelInfo.CompletitionTime);
594		LocalSound('weapons/rocklx');
595		sp_state = 10;
596	}
597
598	if (sp_state == 2)
599	{
600		cnt_kills[0] += 2;
601
602		if (!(ftoi(btime * 35.0) & 3))
603			LocalSound('weapons/pistol');
604
605		if (cnt_kills[0] >= (ClGame.cl.KillCount * 100) / TotalKills)
606		{
607			cnt_kills[0] = (ClGame.cl.KillCount * 100) / TotalKills;
608			LocalSound('weapons/rocklx');
609			sp_state++;
610		}
611	}
612	else if (sp_state == 4)
613	{
614		cnt_items[0] += 2;
615
616		if (!(ftoi(btime * 35.0) & 3))
617			LocalSound('weapons/pistol');
618
619		if (cnt_items[0] >= (ClGame.cl.ItemCount * 100) / TotalItems)
620		{
621			cnt_items[0] = (ClGame.cl.ItemCount * 100) / TotalItems;
622			LocalSound('weapons/rocklx');
623			sp_state++;
624		}
625	}
626	else if (sp_state == 6)
627	{
628		cnt_secret[0] += 2;
629
630		if (!(ftoi(btime * 35.0) & 3))
631			LocalSound('weapons/pistol');
632
633		if (cnt_secret[0] >= (ClGame.cl.SecretCount * 100) / TotalSecret)
634		{
635			cnt_secret[0] = (ClGame.cl.SecretCount * 100) / TotalSecret;
636			LocalSound('weapons/rocklx');
637			sp_state++;
638		}
639	}
640
641	else if (sp_state == 8)
642	{
643		if (!(ftoi(btime * 35.0) & 3))
644			LocalSound('weapons/pistol');
645
646		cnt_time += 3;
647
648		if (cnt_time >= ftoi(ClGame.GLevel.LevelInfo.CompletitionTime))
649		{
650			cnt_time = ftoi(ClGame.GLevel.LevelInfo.CompletitionTime);
651			LocalSound('weapons/rocklx');
652			sp_state++;
653		}
654	}
655	else if (sp_state == 10)
656	{
657		if (ClGame.skipintermission)
658		{
659			LocalSound('weapons/shotgr');
660
661#ifdef DOOM2
662			IM_InitNoState();
663#else
664			IM_InitNextLoc();
665#endif
666		}
667	}
668	else if (sp_state & 1)
669	{
670		cnt_pause -= DeltaTime;
671		if (cnt_pause <= 0.0)
672		{
673			sp_state++;
674			cnt_pause = 1.0;
675		}
676	}
677
678}
679
680//==========================================================================
681//
682//  IM_InitCooperativeStats
683//
684//==========================================================================
685
686void IM_InitCooperativeStats()
687{
688	int i;
689
690	interstate = IMS_Stats;
691	ClGame.skipintermission = 0;
692	ng_state = 1;
693
694	cnt_pause = 1.0;
695
696	for (i = 0; i < MAXPLAYERS; i++)
697	{
698		cnt_kills[i] = 0;
699		cnt_items[i] = 0;
700		cnt_secret[i] = 0;
701		cnt_frags[i] = 0;
702	}
703
704	if (ClGame.cl.Level.bNoIntermission)
705	{
706		interstate = IMS_NoState;
707	}
708}
709
710//==========================================================================
711//
712//  IM_DrawCooperativeStats
713//
714//==========================================================================
715
716void IM_DrawCooperativeStats()
717{
718	int i;
719	int x;
720	int y;
721	int pwidth;
722	picinfo_t info;
723	PlayerReplicationInfo RepInfo;
724
725	pwidth = TextWidth("%");
726
727	IM_DrawLF();
728
729	SetTextAlign(hright, vtop);
730	// draw stat titles (top line)
731	DrawText(NG_STATSX + NG_SPACINGX, NG_STATSY, "kills");
732
733	DrawText(NG_STATSX + 2 * NG_SPACINGX, NG_STATSY, "items");
734
735	DrawText(NG_STATSX + 3 * NG_SPACINGX, NG_STATSY, "scrt");
736
737	DrawText(NG_STATSX + 4 * NG_SPACINGX, NG_STATSY, "frgs");
738
739	// draw stats
740	y = NG_STATSY + TextHeight("kills");
741
742	foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
743	{
744		i = RepInfo.PlayerNum;
745		x = NG_STATSX;
746		R_GetPicInfo(p[i], &info);
747		DrawPic(x - info.width, y, p[i]);
748
749		if (i == ClGame.cl.ClientNum)
750			DrawPic(x - info.width, y, star);
751
752		x += NG_SPACINGX;
753		IM_DrawPercent(x - pwidth, y + 10, cnt_kills[i]);
754		x += NG_SPACINGX;
755		IM_DrawPercent(x - pwidth, y + 10, cnt_items[i]);
756		x += NG_SPACINGX;
757		IM_DrawPercent(x - pwidth, y + 10, cnt_secret[i]);
758		x += NG_SPACINGX;
759
760		SetTextAlign(hright, vtop);
761		DrawText(x, y + 10, va("%d", cnt_frags[i]));
762
763		y += WI_SPACINGY;
764	}
765}
766
767//==========================================================================
768//
769//  IM_UpdateCooperativeStats
770//
771//==========================================================================
772
773void IM_UpdateCooperativeStats(float DeltaTime)
774{
775	int fsum;
776	PlayerReplicationInfo	RepInfo;
777
778	bool stillticking;
779
780	if (ClGame.skipintermission && ng_state != 10)
781	{
782		ClGame.skipintermission = 0;
783
784		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
785		{
786			cnt_kills[RepInfo.PlayerNum] = (RepInfo.KillCount * 100) / TotalKills;
787			cnt_items[RepInfo.PlayerNum] = (RepInfo.ItemCount * 100) / TotalItems;
788			cnt_secret[RepInfo.PlayerNum] = (RepInfo.SecretCount * 100) / TotalSecret;
789			cnt_frags[RepInfo.PlayerNum] = RepInfo.Frags;
790		}
791		LocalSound('weapons/rocklx');
792		ng_state = 10;
793	}
794
795	if (ng_state == 2)
796	{
797		if (!(ftoi(btime * 35.0) & 3))
798			LocalSound('weapons/pistol');
799
800		stillticking = false;
801
802		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
803		{
804			cnt_kills[RepInfo.PlayerNum] += 2;
805
806			if (cnt_kills[RepInfo.PlayerNum] >= (RepInfo.KillCount * 100) / TotalKills)
807				cnt_kills[RepInfo.PlayerNum] = (RepInfo.KillCount * 100) / TotalKills;
808			else
809				stillticking = true;
810		}
811
812		if (!stillticking)
813		{
814			LocalSound('weapons/rocklx');
815			ng_state++;
816		}
817	}
818	else if (ng_state == 4)
819	{
820		if (!(ftoi(btime * 35.0) & 3))
821			LocalSound('weapons/pistol');
822
823		stillticking = false;
824
825		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
826		{
827			cnt_items[RepInfo.PlayerNum] += 2;
828			if (cnt_items[RepInfo.PlayerNum] >= (RepInfo.ItemCount * 100) / TotalItems)
829				cnt_items[RepInfo.PlayerNum] = (RepInfo.ItemCount * 100) / TotalItems;
830			else
831				stillticking = true;
832		}
833		if (!stillticking)
834		{
835			LocalSound('weapons/rocklx');
836			ng_state++;
837		}
838	}
839	else if (ng_state == 6)
840	{
841		if (!(ftoi(btime * 35.0) & 3))
842			LocalSound('weapons/pistol');
843
844		stillticking = false;
845
846		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
847		{
848			cnt_secret[RepInfo.PlayerNum] += 2;
849
850			if (cnt_secret[RepInfo.PlayerNum] >= RepInfo.SecretCount * 100 / TotalSecret)
851				cnt_secret[RepInfo.PlayerNum] = RepInfo.SecretCount * 100 / TotalSecret;
852			else
853				stillticking = true;
854		}
855
856		if (!stillticking)
857		{
858			LocalSound('weapons/rocklx');
859			ng_state += 1;
860		}
861	}
862	else if (ng_state == 8)
863	{
864		if (!(ftoi(btime * 35.0) & 3))
865			LocalSound('weapons/pistol');
866
867		stillticking = false;
868
869		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
870		{
871			cnt_frags[RepInfo.PlayerNum] += 1;
872
873			if (cnt_frags[RepInfo.PlayerNum] >= RepInfo.Frags)
874				cnt_frags[RepInfo.PlayerNum] = RepInfo.Frags;
875			else
876				stillticking = true;
877		}
878
879		if (!stillticking)
880		{
881			LocalSound('*death');
882			ng_state++;
883		}
884	}
885	else if (ng_state == 10)
886	{
887		if (ClGame.skipintermission)
888		{
889			LocalSound('weapons/shotgr');
890#ifdef DOOM2
891			IM_InitNoState();
892#else
893			IM_InitNextLoc();
894#endif
895		}
896	}
897	else if (ng_state & 1)
898	{
899		cnt_pause -= DeltaTime;
900		if (cnt_pause <= 0.0)
901		{
902			ng_state++;
903			cnt_pause = 1.0;
904		}
905	}
906}
907
908//==========================================================================
909//
910//  IM_InitDeathmatchStats
911//
912//==========================================================================
913
914void IM_InitDeathmatchStats()
915{
916	int						i;
917	int						j;
918	PlayerReplicationInfo	RepInfo;
919
920	interstate = IMS_Stats;
921	ClGame.skipintermission = 0;
922	dm_state = 1;
923
924	cnt_pause = 1.0;
925
926	foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
927	{
928		i = RepInfo.PlayerNum;
929		dm_Frags[i] = 0;
930		dm_Deaths[i] = 0;
931	}
932}
933
934//==========================================================================
935//
936//  IM_DrawDeathmatchStats
937//
938//==========================================================================
939
940void IM_DrawDeathmatchStats()
941{
942	int y;
943	picinfo_t info;
944	PlayerReplicationInfo RepInfo;
945
946	IM_DrawLF();
947
948	SetTextAlign(hcentre, vtop);
949	// draw stat titles (top line)
950	DrawText(DM_TOTALSX, DM_MATRIXY - WI_SPACINGY, "FRAGS");
951	DrawText(DM_DEATHSX, DM_MATRIXY - WI_SPACINGY, "DEATHS");
952
953	// draw stats
954	y = DM_MATRIXY;
955
956	foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
957	{
958		if (RepInfo.PlayerNum == ClGame.cl.ClientNum)
959		{
960			R_GetPicInfo(star, &info);
961			DrawPic(DM_MATRIXX - info.width - 5, y - 8, star);
962		}
963		SetTextAlign(hleft, vtop);
964		SetFont('smallfont');
965		DrawText(DM_MATRIXX, y + 5, RepInfo.PlayerName);
966		SetTextAlign(hright, vtop);
967		SetFont('bigfont');
968		DrawText(DM_TOTALSX, y, va("%d", dm_Frags[RepInfo.PlayerNum]));
969		DrawText(DM_DEATHSX, y, va("%d", dm_Deaths[RepInfo.PlayerNum]));
970		y += 24;
971	}
972}
973
974//==========================================================================
975//
976//  IM_UpdateDeathmatchStats
977//
978//==========================================================================
979
980void IM_UpdateDeathmatchStats(float DeltaTime)
981{
982	int i;
983
984	bool stillticking;
985	PlayerReplicationInfo	RepInfo;
986
987	if (ClGame.skipintermission && dm_state != 4)
988	{
989		ClGame.skipintermission = 0;
990
991		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
992		{
993			dm_Frags[RepInfo.PlayerNum] = RepInfo.Frags;
994			dm_Deaths[RepInfo.PlayerNum] = RepInfo.Deaths;
995		}
996
997		LocalSound('weapons/rocklx');
998		dm_state = 4;
999	}
1000
1001	if (dm_state == 2)
1002	{
1003		if (!(ftoi(btime * 35.0) & 3))
1004			LocalSound('weapons/pistol');
1005
1006		stillticking = false;
1007
1008		foreach ClGame.GLevel.LevelInfo.AllThinkers(PlayerReplicationInfo, RepInfo)
1009		{
1010			i = RepInfo.PlayerNum;
1011			if (dm_Frags[i] != RepInfo.Frags)
1012			{
1013				if (RepInfo.Frags < 0)
1014					dm_Frags[i]--;
1015				else
1016					dm_Frags[i]++;
1017
1018				stillticking = true;
1019			}
1020			if (dm_Deaths[i] != RepInfo.Deaths)
1021			{
1022				if (RepInfo.Deaths < 0)
1023					dm_Deaths[i]--;
1024				else
1025					dm_Deaths[i]++;
1026
1027				stillticking = true;
1028			}
1029		}
1030		if (!stillticking)
1031		{
1032			LocalSound('weapons/rocklx');
1033			dm_state++;
1034		}
1035
1036	}
1037	else if (dm_state == 4)
1038	{
1039		if (ClGame.skipintermission)
1040		{
1041			LocalSound('misc/gibbed');
1042
1043#ifdef DOOM2
1044			IM_InitNoState();
1045#else
1046			IM_InitNextLoc();
1047#endif
1048		}
1049	}
1050	else if (dm_state & 1)
1051	{
1052		cnt_pause -= DeltaTime;
1053		if (cnt_pause <= 0.0)
1054		{
1055			dm_state++;
1056			cnt_pause = 1.0;
1057		}
1058	}
1059}
1060
1061#ifndef DOOM2
1062
1063//==========================================================================
1064//
1065//  IM_InitNextLoc
1066//
1067//==========================================================================
1068
1069void IM_InitNextLoc()
1070{
1071	interstate = IMS_NextLoc;
1072	ClGame.skipintermission = 0;
1073	cnt = SHOWNEXTLOCDELAY;
1074
1075	IM_InitAnimatedBack();
1076}
1077
1078//==========================================================================
1079//
1080//  IM_DrawNextLoc
1081//
1082//==========================================================================
1083
1084void IM_DrawNextLoc()
1085{
1086	// draws which level you are entering..
1087	IM_DrawEL();
1088}
1089
1090//==========================================================================
1091//
1092//  IM_UpdateNextLoc
1093//
1094//==========================================================================
1095
1096void IM_UpdateNextLoc(float DeltaTime)
1097{
1098	cnt -= DeltaTime;
1099	if (cnt <= 0.0 || ClGame.skipintermission)
1100		IM_InitNoState();
1101}
1102
1103#endif
1104
1105//==========================================================================
1106//
1107//  IM_InitNoState
1108//
1109//==========================================================================
1110
1111void IM_InitNoState()
1112{
1113	interstate = IMS_NoState;
1114	ClGame.skipintermission = 0;
1115	cnt = 0.3;
1116}
1117
1118//==========================================================================
1119//
1120//  IM_DrawNoState
1121//
1122//==========================================================================
1123
1124void IM_DrawNoState()
1125{
1126#ifdef DOOM2
1127	// draws which level you are entering..
1128	if (ClGame.im.LeaveMap != 'map30')
1129	{
1130		IM_DrawEL();
1131	}
1132#else
1133	IM_DrawNextLoc();
1134#endif
1135}
1136
1137//==========================================================================
1138//
1139//  IM_UpdateNoState
1140//
1141//==========================================================================
1142
1143void IM_UpdateNoState(float DeltaTime)
1144{
1145	cnt -= DeltaTime;
1146	if (cnt <= 0.0)
1147	{
1148		IM_UnloadData();
1149		IM_InitText();
1150	}
1151}
1152
1153//==========================================================================
1154//
1155//
1156//
1157//==========================================================================
1158
1159// specifies current state
1160float intertime;
1161
1162float HubCount;
1163string HubText;
1164
1165name TextFlat;
1166int TextPic;
1167
1168//==========================================================================
1169//
1170//  IM_InitText
1171//
1172//==========================================================================
1173
1174void IM_InitText()
1175{
1176	if (ClGame.deathmatch || !ClGame.im.Text)
1177	{
1178		CmdBuf_AddText("TeleportNewMap\n");
1179		interstate = IMS_Done;
1180		Background.State = IntermissionBackground::STATE_Done;
1181		return;
1182	}
1183
1184	if (ClGame.im.bTextIsLump)
1185	{
1186		HubText = LoadTextLump(StrToName(ClGame.im.Text));
1187	}
1188	else
1189	{
1190		HubText = ClGame.im.Text;
1191	}
1192	HubCount = itof(strlen(HubText)) * TEXTSPEED + TEXTWAIT;
1193
1194	TextFlat = '';
1195	TextPic = 0;
1196	if (ClGame.im.TextPic)
1197	{
1198		TextPic = R_RegisterPic(ClGame.im.TextPic);
1199	}
1200	else if (ClGame.im.TextFlat)
1201	{
1202		TextFlat = ClGame.im.TextFlat;
1203	}
1204
1205	interstate = IMS_Text;
1206	intertime = 0.0;
1207
1208	if (ClGame.im.TextMusic)
1209	{
1210		CmdBuf_AddText(va("music loop %n\n", ClGame.im.TextMusic));
1211	}
1212	else
1213	{
1214#ifdef DOOM2
1215#ifdef FIXME
1216		S_StartSong("d_read_m", P_GetCDEnd1Track(), true);
1217#else
1218		CmdBuf_AddText("music loop d_read_m\n");
1219#endif
1220#else
1221#ifdef FIXME
1222		S_StartSong("D_VICTOR", P_GetCDEnd1Track(), true);
1223#else
1224		CmdBuf_AddText("music loop D_VICTOR\n");
1225#endif
1226#endif
1227	}
1228}
1229
1230//==========================================================================
1231//
1232//  IM_DrawText
1233//
1234//==========================================================================
1235
1236void IM_DrawText()
1237{
1238	int count;
1239
1240	// erase the entire screen to a tiled background
1241	if (TextPic)
1242	{
1243		DrawFullScreenPic(TextPic);
1244	}
1245	else if (TextFlat)
1246	{
1247		FillRectWithFlat(0, 0, 640, 480, TextFlat);
1248	}
1249	else
1250	{
1251		R_FillRect(0, 0, 640, 480, 0);
1252	}
1253
1254	// draw some of the text onto the screen
1255	count = ftoi((intertime - 0.3) / TEXTSPEED);
1256	if (count < 0)
1257		count = 0;
1258	if (count > strlen(HubText))
1259		count = strlen(HubText);
1260
1261	SetFont('smallfont');
1262	SetTextAlign(hleft, vtop);
1263	DrawText(170, 150, substr(HubText, 0, count));
1264}
1265
1266//==========================================================================
1267//
1268//  IM_UpdateText
1269//
1270//==========================================================================
1271
1272void IM_UpdateText(float DeltaTime)
1273{
1274	intertime += DeltaTime;
1275	if (ClGame.skipintermission || intertime > HubCount)
1276	{
1277		ClGame.skipintermission = false;
1278		CmdBuf_AddText("TeleportNewMap\n");
1279		interstate = IMS_Done;
1280		Background.State = IntermissionBackground::STATE_Done;
1281	}
1282}
1283
1284//==========================================================================
1285//
1286//	OnVisibilityChanged
1287//
1288//==========================================================================
1289
1290void OnVisibilityChanged(bool bNewVisibility)
1291{
1292	bTickEnabled = bNewVisibility;
1293	if (Background)
1294	{
1295		Background.bTickEnabled = bNewVisibility;
1296	}
1297}
1298
1299defaultproperties
1300{
1301	bTickEnabled = true;
1302	Focusable = true;
1303	Width = 640;
1304	Height = 480;
1305}
1306