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