1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * Starts up new scenes.
22  */
23 
24 #include "tinsel/actors.h"
25 #include "tinsel/anim.h"
26 #include "tinsel/background.h"
27 #include "tinsel/config.h"
28 #include "tinsel/cursor.h"
29 #include "tinsel/dialogs.h"
30 #include "tinsel/dw.h"
31 #include "tinsel/graphics.h"
32 #include "tinsel/handle.h"
33 #include "tinsel/film.h"
34 #include "tinsel/font.h"
35 #include "tinsel/mareels.h"
36 #include "tinsel/move.h"
37 #include "tinsel/music.h"
38 #include "tinsel/object.h"
39 #include "tinsel/pcode.h"
40 #include "tinsel/pid.h"	// process IDs
41 #include "tinsel/play.h"
42 #include "tinsel/polygons.h"
43 #include "tinsel/rince.h"
44 #include "tinsel/sched.h"
45 #include "tinsel/scn.h"
46 #include "tinsel/scroll.h"
47 #include "tinsel/sound.h"	// stopAllSamples()
48 #include "tinsel/sysvar.h"
49 #include "tinsel/token.h"
50 
51 #include "common/textconsole.h"
52 
53 namespace Tinsel {
54 
55 //----------------- EXTERNAL FUNCTIONS ---------------------
56 
57 // in BG.C
58 extern void DropBackground();
59 
60 // in EFFECT.C
61 extern void EffectPolyProcess(CORO_PARAM, const void *);
62 
63 // in PDISPLAY.C
64 #ifdef DEBUG
65 extern void CursorPositionProcess(CORO_PARAM, const void *);
66 #endif
67 extern void TagProcess(CORO_PARAM, const void *);
68 extern void PointProcess(CORO_PARAM, const void *);
69 extern void EnableTags();
70 
71 
72 //----------------- LOCAL DEFINES --------------------
73 
74 #include "common/pack-start.h"	// START STRUCT PACKING
75 
76 /** scene structure - one per scene */
77 struct SCENE_STRUC {
78 	int32 defRefer;		// Default refer direction (REFTYPE)
79 	SCNHANDLE hSceneScript;	// handle to scene script
80 	SCNHANDLE hSceneDesc;	// handle to scene description
81 	int32 numEntrance;	// number of entrances in this scene
82 	SCNHANDLE hEntrance;	// handle to table of entrances
83 	int32 numPoly;		// number of various polygons in this scene
84 	SCNHANDLE hPoly;	// handle to table of polygons
85 	int32 numTaggedActor;	// number of tagged actors in this scene
86 	SCNHANDLE hTaggedActor;	// handle to table of tagged actors
87 	int32 numProcess;	// number of processes in this scene
88 	SCNHANDLE hProcess;	// handle to table of processes
89 	SCNHANDLE hMusicScript;	// handle to music script data - Tinsel 2 only
90 	SCNHANDLE hMusicSegment;// handle to music segments - Tinsel 2 only
91 } PACKED_STRUCT;
92 
93 /** entrance structure - one per entrance */
94 struct ENTRANCE_STRUC {
95 	int32 eNumber;		///< entrance number
96 	SCNHANDLE hScript;	///< handle to entrance script
97 	// Tinsel 2 fields
98 	SCNHANDLE hEntDesc;	// handle to entrance description
99 	uint32 flags;
100 } PACKED_STRUCT;
101 
102 #include "common/pack-end.h"	// END STRUCT PACKING
103 
104 
105 //----------------- LOCAL GLOBAL DATA --------------------
106 
107 // FIXME: Avoid non-const global vars
108 
109 #ifdef DEBUG
110 static bool g_ShowPosition = false;	// Set when showpos() has been called
111 #endif
112 
113 int g_sceneCtr = 0;
114 static int g_initialMyEscape;
115 
116 static SCNHANDLE g_SceneHandle = 0;	// Current scene handle - stored in case of Save_Scene()
117 
118 SCENE_STRUC g_tempStruc;
119 
120 struct TP_INIT {
121 	SCNHANDLE hTinselCode;		// Code
122 	TINSEL_EVENT event;			// Triggering event
123 };
124 
GetSceneStruc(const byte * pStruc)125 const SCENE_STRUC *GetSceneStruc(const byte *pStruc) {
126 	if (TinselVersion == TINSEL_V2)
127 		return (const SCENE_STRUC *)pStruc;
128 
129 	// Copy appropriate fields into tempStruc, and return a pointer to it
130 	const byte *p = pStruc;
131 	memset(&g_tempStruc, 0, sizeof(SCENE_STRUC));
132 
133 	g_tempStruc.numEntrance    = READ_UINT32(p); p += sizeof(uint32);
134 	g_tempStruc.numPoly        = READ_UINT32(p); p += sizeof(uint32);
135 	g_tempStruc.numTaggedActor = READ_UINT32(p); p += sizeof(uint32);
136 	g_tempStruc.defRefer       = READ_UINT32(p); p += sizeof(uint32);
137 	g_tempStruc.hSceneScript   = READ_UINT32(p); p += sizeof(uint32);
138 	g_tempStruc.hEntrance      = READ_UINT32(p); p += sizeof(uint32);
139 	g_tempStruc.hPoly          = READ_UINT32(p); p += sizeof(uint32);
140 	g_tempStruc.hTaggedActor   = READ_UINT32(p); p += sizeof(uint32);
141 
142 	return &g_tempStruc;
143 }
144 
145 
146 /**
147  * Started up for scene script and entrance script.
148  */
SceneTinselProcess(CORO_PARAM,const void * param)149 static void SceneTinselProcess(CORO_PARAM, const void *param) {
150 	// COROUTINE
151 	CORO_BEGIN_CONTEXT;
152 		INT_CONTEXT *pic;
153 		const TP_INIT *pInit;
154 		int myEscape;
155 	CORO_END_CONTEXT(_ctx);
156 
157 	CORO_BEGIN_CODE(_ctx);
158 
159 	// The following myEscape value setting is used for enabling title screen skipping in DW1
160 	if (TinselV1 && (g_sceneCtr == 1)) g_initialMyEscape = GetEscEvents();
161 	// DW1 PSX has its own scene skipping script code for scenes 2 and 3 (bug #3541542).
162 	// Same goes for DW1 Mac.
163 	_ctx->myEscape = (TinselV1 && (g_sceneCtr < ((TinselV1PSX || TinselV1Mac) ? 2 : 4))) ? g_initialMyEscape : 0;
164 
165 	// get the stuff copied to process when it was created
166 	_ctx->pInit = (const TP_INIT *)param;
167 	assert(_ctx->pInit);
168 	assert(_ctx->pInit->hTinselCode);		// Must have some code to run
169 
170 	_ctx->pic = InitInterpretContext(GS_SCENE,
171 		FROM_32(_ctx->pInit->hTinselCode),
172 		TinselV2 ? _ctx->pInit->event : NOEVENT,
173 		NOPOLY,			// No polygon
174 		0,				// No actor
175 		NULL,			// No object
176 		_ctx->myEscape);
177 	CORO_INVOKE_1(Interpret, _ctx->pic);
178 
179 	CORO_END_CODE;
180 }
181 
182 /**
183  * Start up a SceneTinselProcess() running the scene
184  * script for various events.
185  */
SendSceneTinselProcess(TINSEL_EVENT event)186 void SendSceneTinselProcess(TINSEL_EVENT event) {
187 	SCENE_STRUC	*ss;
188 
189 	if (g_SceneHandle != 0) {
190 		ss = (SCENE_STRUC *) FindChunk(g_SceneHandle, CHUNK_SCENE);
191 
192 		if (ss->hSceneScript) {
193 			TP_INIT	init;
194 
195 			init.event = event;
196 			init.hTinselCode = ss->hSceneScript;
197 
198 			CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
199 		}
200 	}
201 }
202 
203 
204 /**
205  * Get the SCENE_STRUC
206  * Initialize polygons for the scene
207  * Initialize the actors for this scene
208  * Run the appropriate entrance code (if any)
209  * Get the default refer type
210  */
211 
LoadScene(SCNHANDLE scene,int entry)212 static void LoadScene(SCNHANDLE scene, int entry) {
213 	uint32	i;
214 	TP_INIT init;
215 	const SCENE_STRUC	*ss;
216 	const ENTRANCE_STRUC	*es;
217 
218 	// Scene handle
219 	g_SceneHandle = scene;		// Save scene handle in case of Save_Scene()
220 	LockMem(g_SceneHandle);		// Make sure scene is loaded
221 	LockScene(g_SceneHandle);		// Prevent current scene from being discarded
222 
223 	if (TinselV2) {
224 		// CdPlay() stuff
225 		byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM);
226 		assert(cptr);
227 		i = READ_32(cptr);
228 		assert(i < 512);
229 		cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME);
230 		assert(cptr);
231 		SetCdPlaySceneDetails(i, (const char *)cptr);
232 	}
233 
234 	// Find scene structure
235 	ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
236 	assert(ss != NULL);
237 
238 	if (TinselV2) {
239 		// Music stuff
240 		char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME);
241 		assert(cptr);
242 		_vm->_pcmMusic->setMusicSceneDetails(FROM_32(ss->hMusicScript), FROM_32(ss->hMusicSegment), cptr);
243 	}
244 
245 	if (entry == NO_ENTRY_NUM) {
246 		// Restoring scene
247 
248 		// Initialize all the polygons for this scene
249 		InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), true);
250 
251 		// Initialize the actors for this scene
252 		StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), false);
253 
254 		if (TinselV2)
255 			// Returning from cutscene
256 			SendSceneTinselProcess(RESTORE);
257 
258 	} else {
259 		// Genuine new scene
260 
261 		// Initialize all the polygons for this scene
262 		InitPolygons(FROM_32(ss->hPoly), FROM_32(ss->numPoly), false);
263 
264 		// Initialize the actors for this scene
265 		StartTaggedActors(FROM_32(ss->hTaggedActor), FROM_32(ss->numTaggedActor), true);
266 
267 		// Run the appropriate entrance code (if any)
268 		es = (const ENTRANCE_STRUC *)LockMem(FROM_32(ss->hEntrance));
269 		for (i = 0; i < FROM_32(ss->numEntrance); i++) {
270 			if (FROM_32(es->eNumber) == (uint)entry) {
271 				if (es->hScript) {
272 					init.event = STARTUP;
273 					init.hTinselCode = es->hScript;
274 
275 					CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
276 				}
277 				break;
278 			}
279 
280 			// Move to next entrance
281 			if (TinselV2)
282 				++es;
283 			else
284 				es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
285 
286 		}
287 
288 		if (i == FROM_32(ss->numEntrance))
289 			error("Non-existent scene entry number");
290 
291 		if (ss->hSceneScript) {
292 			init.event = STARTUP;
293 			init.hTinselCode = ss->hSceneScript;
294 
295 			CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
296 		}
297 	}
298 
299 	// Default refer type
300 	SetDefaultRefer(FROM_32(ss->defRefer));
301 
302 	// Scene's processes
303 	SceneProcesses(FROM_32(ss->numProcess), FROM_32(ss->hProcess));
304 }
305 
306 
307 /**
308  * Wrap up the last scene.
309  */
EndScene()310 void EndScene() {
311 	if (g_SceneHandle != 0) {
312 		UnlockScene(g_SceneHandle);
313 		g_SceneHandle = 0;
314 	}
315 
316 	KillInventory();	// Close down any open inventory
317 
318 	DropPolygons();		// No polygons
319 	DropScroll();	// No no-scrolls
320 	DropBackground();	// No background
321 	DropMovers();		// No moving actors
322 	DropCursor();		// No cursor
323 	DropActors();		// No actor reels running
324 	FreeAllTokens();	// No-one has tokens
325 	FreeMostInterpretContexts();	// Only master script still interpreting
326 
327 	if (TinselV2) {
328 		SetSysVar(ISV_DIVERT_ACTOR, 0);
329 		SetSysVar(ISV_GHOST_ACTOR, 0);
330 		SetSysVar(SV_MinimumXoffset, 0);
331 		SetSysVar(SV_MaximumXoffset, 0);
332 		SetSysVar(SV_MinimumYoffset, 0);
333 		SetSysVar(SV_MaximumYoffset, 0);
334 		ResetFontHandles();
335 		NoSoundReels();
336 	}
337 
338 	_vm->_sound->stopAllSamples();		// Kill off any still-running sample
339 	//_vm->_mixer->stopAll();
340 
341 	// init the palette manager
342 	ResetPalAllocator();
343 
344 	// init the object manager
345 	KillAllObjects();
346 
347 	// kill all destructable process
348 	CoroScheduler.killMatchingProcess(PID_DESTROY, PID_DESTROY);
349 }
350 
351 /**
352  *
353  */
PrimeBackground()354 void PrimeBackground() {
355 	// structure for playfields
356 	// FIXME: Avoid non-const global vars
357 	// TODO: We should simply merge this function with InitBackground
358 	//   in order to avoid the static var and the problems associate
359 	//   with it.
360 	static PLAYFIELD playfield[] = {
361 		{	// FIELD WORLD
362 			NULL,		// display list
363 			0,			// init field x
364 			0,			// init field y
365 			0,			// x vel
366 			0,			// y vel
367 			Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),	// clip rect
368 			false		// moved flag
369 		},
370 		{	// FIELD STATUS
371 			NULL,		// display list
372 			0,			// init field x
373 			0,			// init field y
374 			0,			// x vel
375 			0,			// y vel
376 			Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),	// clip rect
377 			false		// moved flag
378 		}
379 	};
380 
381 	// structure for background
382 	static const BACKGND backgnd = {
383 		BLACK,			// sky color
384 		Common::Point(0, 0),	// initial world pos
385 		Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),	// scroll limits
386 		0,				// no background update process
387 		NULL,			// no x scroll table
388 		NULL,			// no y scroll table
389 		2,				// 2 playfields
390 		playfield,		// playfield pointer
391 		false			// no auto-erase
392 	};
393 
394 	InitBackground(&backgnd);
395 }
396 
397 /**
398  * Start up the standard stuff for the next scene.
399  */
400 
PrimeScene()401 void PrimeScene() {
402 	SetNoBlocking(false);
403 	SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
404 
405 	RestartCursor();	// Restart the cursor
406 	if (!TinselV2)
407 		EnableTags();		// Next scene with tags enabled
408 
409 	CoroScheduler.createProcess(PID_SCROLL, ScrollProcess, NULL, 0);
410 	CoroScheduler.createProcess(PID_SCROLL, EffectPolyProcess, NULL, 0);
411 
412 #ifdef DEBUG
413 	if (g_ShowPosition)
414 		CoroScheduler.createProcess(PID_POSITION, CursorPositionProcess, NULL, 0);
415 #endif
416 
417 	CoroScheduler.createProcess(PID_TAG, TagProcess, NULL, 0);
418 	CoroScheduler.createProcess(PID_TAG, PointProcess, NULL, 0);
419 
420 	// init the current background
421 	PrimeBackground();
422 }
423 
424 /**
425  * Wrap up the last scene and start up the next scene.
426  */
427 
StartNewScene(SCNHANDLE scene,int entry)428 void StartNewScene(SCNHANDLE scene, int entry) {
429 	EndScene();	// Wrap up the last scene.
430 
431 	if (TinselV2) {
432 		TouchMoverReels();
433 
434 		LockMem(scene);	// Do CD change before PrimeScene
435 	}
436 
437 	PrimeScene();	// Start up the standard stuff for the next scene.
438 
439 	LoadScene(scene, entry);
440 }
441 
442 #ifdef DEBUG
443 /**
444  * Sets the ShowPosition flag, causing the cursor position process to be
445  * created in each scene.
446  */
447 
setshowpos()448 void setshowpos() {
449 	g_ShowPosition = true;
450 }
451 #endif
452 
453 /**
454  * Return the current scene handle.
455  */
456 
GetSceneHandle()457 SCNHANDLE GetSceneHandle() {
458 	return g_SceneHandle;
459 }
460 
461 /**
462  * DoHailScene
463  */
464 
DoHailScene(SCNHANDLE scene)465 void DoHailScene(SCNHANDLE scene) {
466 	// Find scene structure
467 	const SCENE_STRUC *ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
468 
469 	if (ss != NULL && ss->hSceneScript) {
470 		TP_INIT	init;
471 
472 		init.event = NOEVENT;
473 		init.hTinselCode = ss->hSceneScript;
474 
475 		CoroScheduler.createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
476 	}
477 }
478 
479 /**
480  * WrapScene
481  */
WrapScene()482 void WrapScene() {
483 	SendSceneTinselProcess(CLOSEDOWN);
484 }
485 
486 } // End of namespace Tinsel
487