1 /*
2 ** g_intermission.cpp
3 **
4 **---------------------------------------------------------------------------
5 ** Copyright 2012 Braden Obrzut
6 ** All rights reserved.
7 **
8 ** Redistribution and use in source and binary forms, with or without
9 ** modification, are permitted provided that the following conditions
10 ** are met:
11 **
12 ** 1. Redistributions of source code must retain the above copyright
13 **    notice, this list of conditions and the following disclaimer.
14 ** 2. Redistributions in binary form must reproduce the above copyright
15 **    notice, this list of conditions and the following disclaimer in the
16 **    documentation and/or other materials provided with the distribution.
17 ** 3. The name of the author may not be used to endorse or promote products
18 **    derived from this software without specific prior written permission.
19 **
20 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 **---------------------------------------------------------------------------
31 **
32 **
33 */
34 
35 #include "wl_def.h"
36 #include "id_ca.h"
37 #include "id_in.h"
38 #include "id_sd.h"
39 #include "id_vh.h"
40 #include "g_intermission.h"
41 #include "language.h"
42 #include "r_sprites.h"
43 #include "tarray.h"
44 #include "wl_agent.h"
45 #include "wl_draw.h"
46 #include "wl_game.h"
47 #include "wl_inter.h"
48 #include "wl_menu.h"
49 #include "wl_play.h"
50 #include "thingdef/thingdef.h"
51 
52 static TMap<FName, IntermissionInfo> intermissions;
53 
Find(const FName & name)54 IntermissionInfo *IntermissionInfo::Find(const FName &name)
55 {
56 	return &intermissions[name];
57 }
58 
Clear()59 void IntermissionInfo::Clear()
60 {
61 	for(unsigned int i = 0;i < Actions.Size();++i)
62 		delete Actions[i].action;
63 	Actions.Clear();
64 }
65 
66 ////////////////////////////////////////////////////////////////////////////////
67 
68 void StartTravel ();
69 void FinishTravel ();
70 
71 static bool intermissionMapLoaded = false;
72 static bool exitOnAck;
73 
ClearStatusbar()74 static void ClearStatusbar()
75 {
76 	DrawPlayScreen();
77 
78 	FTexture *borderTex = TexMan(levelInfo->GetBorderTexture());
79 	VWB_DrawFill(borderTex, statusbarx, statusbary2+CleanYfac, screenWidth-statusbarx, screenHeight);
80 }
81 
WaitIntermission(unsigned int time)82 static bool WaitIntermission(unsigned int time)
83 {
84 	if(time)
85 	{
86 		return IN_UserInput(time);
87 	}
88 	else
89 	{
90 		IN_ClearKeysDown ();
91 		IN_Ack ();
92 		return true;
93 	}
94 }
95 
ShowImage(IntermissionAction * image,bool drawonly)96 static bool ShowImage(IntermissionAction *image, bool drawonly)
97 {
98 	if(!image->Music.IsEmpty())
99 		StartCPMusic(image->Music);
100 
101 	if(!image->Palette.IsEmpty())
102 	{
103 		if(image->Palette.CompareNoCase("$GamePalette") == 0)
104 			VL_ReadPalette(gameinfo.GamePalette);
105 		else
106 			VL_ReadPalette(image->Palette);
107 	}
108 
109 	static FTextureID background;
110 	static bool tileBackground = false;
111 	static IntermissionAction::BackgroundType type = IntermissionAction::NORMAL;
112 
113 	// High Scores and such need special handling
114 	if(image->Type != IntermissionAction::UNSET)
115 	{
116 		type = image->Type;
117 	}
118 	if(type == IntermissionAction::NORMAL && image->Background.isValid())
119 	{
120 		background = image->Background;
121 		tileBackground = image->BackgroundTile;
122 	}
123 
124 	intermissionMapLoaded = false;
125 	switch(type)
126 	{
127 		default:
128 			if(!tileBackground)
129 				CA_CacheScreen(TexMan(background));
130 			else
131 				VWB_DrawFill(TexMan(background), 0, 0, screenWidth, screenHeight);
132 			break;
133 		case IntermissionAction::HIGHSCORES:
134 			DrawHighScores();
135 			break;
136 		case IntermissionAction::TITLEPAGE:
137 			background = TexMan.CheckForTexture(gameinfo.TitlePage, FTexture::TEX_Any);
138 			if(!gameinfo.TitlePalette.IsEmpty())
139 				VL_ReadPalette(gameinfo.TitlePalette);
140 			CA_CacheScreen(TexMan(background));
141 			break;
142 		case IntermissionAction::LOADMAP:
143 			if(image->MapName.IsNotEmpty())
144 			{
145 				strncpy(gamestate.mapname, image->MapName, 8);
146 				StartTravel();
147 				SetupGameLevel();
148 				FinishTravel();
149 				// Drop weapon
150 				players[0].SetPSprite(NULL, player_t::ps_weapon);
151 				PreloadGraphics(true);
152 				gamestate.victoryflag = true;
153 			}
154 			intermissionMapLoaded = true;
155 			ThreeDRefresh();
156 			ClearStatusbar();
157 			break;
158 	}
159 
160 	for(unsigned int i = 0;i < image->Draw.Size();++i)
161 	{
162 		VWB_DrawGraphic(TexMan(image->Draw[i].Image), image->Draw[i].X, image->Draw[i].Y);
163 	}
164 
165 	if(!drawonly)
166 	{
167 		VW_UpdateScreen();
168 		return WaitIntermission(image->Time);
169 	}
170 	return false;
171 }
172 
173 
DrawCastName(CastIntermissionAction * cast)174 static void DrawCastName(CastIntermissionAction *cast)
175 {
176 	int width = BigFont->StringWidth(cast->Name);
177 	screen->DrawText(BigFont, gameinfo.FontColors[GameInfo::DIALOG],
178 		(screenWidth - width*CleanXfac)/2, statusbary2 + (screenHeight - statusbary2 - BigFont->GetHeight())/2,
179 		cast->Name,
180 		DTA_CleanNoMove, true,
181 		TAG_DONE
182 	);
183 }
R_CastZoomer(const Frame * frame,CastIntermissionAction * cast)184 static bool R_CastZoomer(const Frame *frame, CastIntermissionAction *cast)
185 {
186 	// This may appear to animate faster than vanilla, but I'm fairly sure
187 	// that's because while the time on screen is adaptive, the frame durations
188 	// were decremented by one each frame.
189 	TObjPtr<SpriteZoomer> zoomer = new SpriteZoomer(frame, 224);
190 	do
191 	{
192 		for(unsigned int t = tics;zoomer && t-- > 0;)
193 			zoomer->Tick();
194 		if(!zoomer)
195 			break;
196 
197 		if(intermissionMapLoaded)
198 			ThreeDRefresh();
199 		else
200 		{
201 			// Unlike a 3D view, we will overwrite the whole screen here
202 			ShowImage(cast, true);
203 			DrawCastName(cast);
204 		}
205 		zoomer->Draw();
206 		VH_UpdateScreen();
207 		IN_ProcessEvents();
208 		if(Keyboard[sc_Space] || Keyboard[sc_Escape] || Keyboard[sc_Enter])
209 		{
210 			bool done = Keyboard[sc_Escape] || Keyboard[sc_Enter];
211 			Keyboard[sc_Space] = Keyboard[sc_Escape] = Keyboard[sc_Enter] = false;
212 			zoomer->Destroy();
213 			if(done)
214 				return true;
215 			break;
216 		}
217 		CalcTics();
218 	}
219 	while(true);
220 	return false;
221 }
ShowCast(CastIntermissionAction * cast)222 static bool ShowCast(CastIntermissionAction *cast)
223 {
224 	ClearStatusbar();
225 	DrawCastName(cast);
226 
227 	SD_PlaySound(cast->Class->GetDefault()->seesound);
228 	const Frame *frame = cast->Class->FindState(NAME_See);
229 	return R_CastZoomer(frame, cast);
230 }
231 
ShowFader(FaderIntermissionAction * fader)232 static void ShowFader(FaderIntermissionAction *fader)
233 {
234 	switch(fader->Fade)
235 	{
236 		case FaderIntermissionAction::FADEIN:
237 			ShowImage(fader, true);
238 			VL_FadeIn(0, 255, fader->Time);
239 			break;
240 		case FaderIntermissionAction::FADEOUT:
241 			// We want to hold whatever may have been drawn in the previous page during the fade, so we don't need to draw.
242 			VL_FadeOut(0, 255, 0, 0, 0, fader->Time);
243 			break;
244 	}
245 }
246 
ShowTextScreen(TextScreenIntermissionAction * textscreen,bool demoMode)247 static bool ShowTextScreen(TextScreenIntermissionAction *textscreen, bool demoMode)
248 {
249 	if(textscreen->TextSpeed)
250 		Printf("Warning: Text screen has a non-zero textspeed which isn't supported at this time.\n");
251 
252 	ShowImage(textscreen, true);
253 
254 	if(textscreen->TextDelay)
255 	{
256 		if(WaitIntermission(textscreen->TextDelay) && demoMode)
257 			return true;
258 	}
259 
260 	py = textscreen->PrintY;
261 	for(unsigned int i = 0;i < textscreen->Text.Size();++i)
262 	{
263 		px = textscreen->PrintX;
264 
265 		FString str = textscreen->Text[i];
266 		if(str[0] == '$')
267 			str = language[str.Mid(1)];
268 
269 		DrawMultiLineText(str, textscreen->TextFont, textscreen->TextColor, textscreen->Alignment, textscreen->Anchor);
270 	}
271 
272 	// This really only makes sense to use if trying to display text immediately.
273 	if(textscreen->FadeTime)
274 	{
275 		VL_FadeIn(0, 255, textscreen->FadeTime);
276 	}
277 
278 	VW_UpdateScreen();
279 	return WaitIntermission(textscreen->Time);
280 }
281 
ShowIntermission(const IntermissionInfo * intermission,bool demoMode)282 bool ShowIntermission(const IntermissionInfo *intermission, bool demoMode)
283 {
284 	exitOnAck = demoMode;
285 	bool gototitle = false;
286 	bool acked = false;
287 
288 	// For a cast call we want the bar area to display the names
289 	if(viewsize > 20)
290 	{
291 		const int oldviewsize = viewsize;
292 		NewViewSize(20);
293 		viewsize = oldviewsize;
294 	}
295 
296 	do
297 	{
298 		for(unsigned int i = 0;i < intermission->Actions.Size();++i)
299 		{
300 			switch(intermission->Actions[i].type)
301 			{
302 				default:
303 				case IntermissionInfo::IMAGE:
304 					acked = ShowImage(intermission->Actions[i].action, false);
305 					break;
306 				case IntermissionInfo::CAST:
307 					acked = gototitle = ShowCast((CastIntermissionAction*)intermission->Actions[i].action);
308 					break;
309 				case IntermissionInfo::FADER:
310 					ShowFader((FaderIntermissionAction*)intermission->Actions[i].action);
311 					break;
312 				case IntermissionInfo::GOTOTITLE:
313 					gototitle = true;
314 					break;
315 				case IntermissionInfo::TEXTSCREEN:
316 					acked = ShowTextScreen((TextScreenIntermissionAction*)intermission->Actions[i].action, demoMode);
317 					break;
318 				case IntermissionInfo::VICTORYSTATS:
319 					Victory(true);
320 					break;
321 			}
322 
323 			if(demoMode ? acked : gototitle)
324 				goto EscSequence;
325 		}
326 		if(intermission->Link != NAME_None)
327 			intermission = IntermissionInfo::Find(intermission->Link);
328 		else
329 			break;
330 	}
331 	while(intermission);
332 EscSequence:
333 
334 	// If we changed the view size, we should reset it now.
335 	if(viewsize > 20)
336 		NewViewSize(viewsize);
337 
338 	if(!gototitle && !demoMode)
339 	{
340 		// Hold at the final page until esc is pressed
341 		IN_ClearKeysDown();
342 
343 		ControlInfo ci;
344 		do
345 		{
346 			LastScan = 0;
347 			ReadAnyControl(&ci);
348 		}
349 		while(LastScan != sc_Escape && LastScan != sc_Enter && !ci.button1);
350 	}
351 
352 	if(demoMode)
353 	{
354 		if(acked)
355 		{
356 			VW_FadeOut();
357 			VL_ReadPalette(gameinfo.GamePalette);
358 			return false;
359 		}
360 		else
361 			return true;
362 	}
363 	else
364 	{
365 		VL_ReadPalette(gameinfo.GamePalette);
366 		return !gototitle;
367 	}
368 }
369