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  * Plays films within a scene, takes into account the actor in each 'column'.								|
22  */
23 
24 #include "common/coroutines.h"
25 #include "tinsel/actors.h"
26 #include "tinsel/background.h"
27 #include "tinsel/dw.h"
28 #include "tinsel/film.h"
29 #include "tinsel/handle.h"
30 #include "tinsel/multiobj.h"
31 #include "tinsel/object.h"
32 #include "tinsel/pid.h"
33 #include "tinsel/play.h"
34 #include "tinsel/polygons.h"
35 #include "tinsel/movers.h"
36 #include "tinsel/sched.h"
37 #include "tinsel/scn.h"
38 #include "tinsel/sound.h"
39 #include "tinsel/timers.h"
40 #include "tinsel/tinlib.h"	// Stand()
41 
42 namespace Tinsel {
43 
44 struct PPINIT {
45 	SCNHANDLE hFilm;	// The 'film'
46 	int16	x;			// } Co-ordinates from the play()
47 	int16	y;			// } - set to (-1, -1) if none.
48 	int16	z;			// normally 0, set if from restore
49 	int16	speed;		// Film speed
50 	int16	actorid;	// Set if called from an actor code block
51 	uint8	splay;		// Set if called from splay()
52 	uint8	bTop;		// Set if called from topplay()
53 	uint8	bRestore;
54 	int16	sf;			// SlowFactor - only used for moving actors
55 	int16	column;		// Column number, first column = 0
56 
57 	uint8	escOn;
58 	int32	myescEvent;
59 
60 	OBJECT** playfield;	// TinselV3, the playfield to insert the film
61 };
62 
63 //----------------- LOCAL GLOBAL DATA --------------------
64 
65 // These vars are reset upon engine destruction
66 
67 static SOUNDREELS g_soundReels[MAX_SOUNDREELS];
68 static int g_soundReelNumbers[MAX_SOUNDREELS];
69 
70 static int g_soundReelWait;
71 static int firstColZ = 0;     // Z-position of column zero
72 static int32 fColZfactor = 0; // Z-factor of column zero's actor
73 static int baseZposn;         // Z-position of column zero
74 static uint32 baseZfact;      // Z-factor of column zero's actor
75 
76 //-------------------- METHODS ----------------------
77 
ResetVarsPlay()78 void ResetVarsPlay() {
79 	memset(g_soundReels, 0, sizeof(g_soundReels));
80 	memset(g_soundReelNumbers, 0, sizeof(g_soundReelNumbers));
81 	g_soundReelWait = 0;
82 	firstColZ = 0;
83 	fColZfactor = 0;
84 	baseZposn = 0;
85 	baseZfact = 0;
86 }
87 
88 /**
89  * Poke the background palette into an image.
90  */
PokeInPalette(SCNHANDLE hMulFrame)91 static void PokeInPalette(SCNHANDLE hMulFrame) {
92 	const FRAME *pFrame;		// Pointer to frame
93 	IMAGE *pim;		// Pointer to image
94 
95 	// Could be an empty column
96 	if (hMulFrame) {
97 		pFrame = (const FRAME *)_vm->_handle->LockMem(hMulFrame);
98 
99 		// get pointer to image
100 		pim = (IMAGE *)_vm->_handle->LockMem(READ_32(pFrame)); // handle to image
101 
102 		pim->hImgPal = TO_32(_vm->_bg->BgPal());
103 	}
104 }
105 
106 /**
107  * Poke the background palette into an image.
108  */
PokeInPalette(const MULTI_INIT * pmi)109 void PokeInPalette(const MULTI_INIT *pmi) {
110 	FRAME	*pFrame;		// Pointer to frame
111 	IMAGE	*pim;			// Pointer to image
112 
113 	// Could be an empty column
114 	if (pmi->hMulFrame) {
115 		pFrame = (FRAME *)_vm->_handle->LockMem(FROM_32(pmi->hMulFrame));
116 
117 		// get pointer to image
118 		pim = (IMAGE *)_vm->_handle->LockMem(READ_32(pFrame)); // handle to image
119 
120 		pim->hImgPal = TO_32(_vm->_bg->BgPal());
121 	}
122 }
123 
NoNameFunc(int actorID,bool bNewMover)124 int32 NoNameFunc(int actorID, bool bNewMover) {
125 	PMOVER	pActor;
126 	int32	retval;
127 
128 	pActor = GetMover(actorID);
129 
130 	if (pActor != NULL && !bNewMover) {
131 		// If no path, just use first path in the scene
132 		if (pActor->hCpath == NOPOLY)
133 			retval = GetPolyZfactor(FirstPathPoly());
134 		else
135 			retval = GetPolyZfactor(pActor->hCpath);
136 	} else {
137 		switch (_vm->_actor->actorMaskType(actorID)) {
138 		case ACT_DEFAULT:
139 			retval = 0;
140 			break;
141 		case ACT_MASK:
142 			retval = 0;
143 			break;
144 		case ACT_ALWAYS:
145 			retval = 10;
146 			break;
147 		default:
148 			retval = _vm->_actor->actorMaskType(actorID);
149 			break;
150 		}
151 	}
152 
153 	return retval;
154 }
155 
GetReel(SCNHANDLE hFilm,int column)156 static FREEL *GetReel(SCNHANDLE hFilm, int column) {
157 	FILM *pFilm = (FILM *)_vm->_handle->LockMem(hFilm);
158 
159 	return &pFilm->reels[column];
160 }
161 
RegisterSoundReel(SCNHANDLE hFilm,int column,int actorCol)162 static int RegisterSoundReel(SCNHANDLE hFilm, int column, int actorCol) {
163 	int i;
164 
165 	for (i = 0; i < MAX_SOUNDREELS; i++) {
166 		// Should assert this doesn't happen, but let's be tolerant
167 		if (g_soundReels[i].hFilm == hFilm && g_soundReels[i].column == column)
168 			break;
169 
170 		if (!g_soundReels[i].hFilm) {
171 			g_soundReels[i].hFilm = hFilm;
172 			g_soundReels[i].column = column;
173 			g_soundReels[i].actorCol = actorCol;
174 			break;
175 		}
176 	}
177 
178 	if (i == MAX_SOUNDREELS)
179 		error("Out of sound reels in RegisterSoundReel()");
180 
181 	g_soundReelNumbers[i]++;
182 	return i;
183 }
184 
NoSoundReels()185 void NoSoundReels() {
186 	memset(g_soundReels, 0, sizeof(g_soundReels));
187 	g_soundReelWait = 0;
188 }
189 
DeRegisterSoundReel(SCNHANDLE hFilm,int column)190 static void DeRegisterSoundReel(SCNHANDLE hFilm, int column) {
191 	for (int i = 0; i < MAX_SOUNDREELS; i++) {
192 		// Should assert this doesn't happen, but let's be tolerant
193 		if (g_soundReels[i].hFilm == hFilm && g_soundReels[i].column == column) {
194 			g_soundReels[i].hFilm = 0;
195 			break;
196 		}
197 	}
198 }
199 
SaveSoundReels(PSOUNDREELS psr)200 void SaveSoundReels(PSOUNDREELS psr) {
201 	for (int i = 0; i < MAX_SOUNDREELS; i++) {
202 		if (_vm->_handle->IsCdPlayHandle(g_soundReels[i].hFilm))
203 			g_soundReels[i].hFilm = 0;
204 	}
205 
206 	memcpy(psr, g_soundReels, sizeof(g_soundReels));
207 }
208 
RestoreSoundReels(PSOUNDREELS psr)209 void RestoreSoundReels(PSOUNDREELS psr) {
210 	memcpy(g_soundReels, psr, sizeof(g_soundReels));
211 }
212 
GetZfactor(int actorID,PMOVER pMover,bool bNewMover)213 static uint32 GetZfactor(int actorID, PMOVER pMover, bool bNewMover) {
214 	if (pMover != NULL && bNewMover == false) {
215 		// If no path, just use first path in the scene
216 		if (pMover->hCpath == NOPOLY)
217 			return GetPolyZfactor(FirstPathPoly());
218 		else
219 			return GetPolyZfactor(pMover->hCpath);
220 	} else {
221 		return _vm->_actor->GetActorZfactor(actorID);
222 	}
223 }
224 
225 /**
226  * Handles reels with sound id.
227  * @param hFilm				The 'film'
228  * @param column			Column number, first column = 0
229  * @param speed				Film speed
230  */
SoundReel(CORO_PARAM,SCNHANDLE hFilm,int column,int speed,int myescEvent,int actorCol)231 static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed,
232 					int myescEvent, int actorCol) {
233 	FILM *pFilm;
234 	FREEL *pReel;
235 	ANI_SCRIPT *pAni;
236 
237 	short x, y;
238 
239 	CORO_BEGIN_CONTEXT;
240 		int myId;
241 		int myNum;
242 		int frameNumber;
243 		int speed;
244 		int sampleNumber;
245 		bool bFinished;
246 		bool bLooped;
247 		int reelActor;
248 	CORO_END_CONTEXT(_ctx);
249 
250 	CORO_BEGIN_CODE(_ctx);
251 
252 	if (actorCol) {
253 		PMULTI_INIT pmi;		// MULTI_INIT structure
254 
255 		pReel = GetReel(hFilm, actorCol - 1);
256 		pmi = (PMULTI_INIT)_vm->_handle->LockMem(FROM_32(pReel->mobj));
257 		_ctx->reelActor = (int32)FROM_32(pmi->mulID);
258 	} else
259 		_ctx->reelActor = 0;
260 
261 	_ctx->frameNumber = 0;
262 	_ctx->speed = speed;
263 	_ctx->sampleNumber = 0;
264 	_ctx->bFinished = false;
265 	_ctx->bLooped = false;
266 	_ctx->myId = RegisterSoundReel(hFilm, column, actorCol);
267 	_ctx->myNum = g_soundReelNumbers[_ctx->myId];
268 
269 	do {
270 		pFilm = (FILM *)_vm->_handle->LockMem(hFilm);
271 		pReel = &pFilm->reels[column];
272 
273 		pAni = (ANI_SCRIPT *)_vm->_handle->LockMem(FROM_32(pReel->script));
274 
275 		if (_ctx->speed == -1) {
276 			_ctx->speed = (ONE_SECOND/FROM_32(pFilm->frate));
277 
278 			// Restored reel
279 			for (;;) {
280 				if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_END)
281 					break;
282 				else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_JUMP) {
283 					_ctx->frameNumber++;
284 					_ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op);
285 					break;
286 				}
287 				// Could check for the other stuff here
288 				// but they really dont happen
289 				// OH YES THEY DO
290 				else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTX
291 					||	 FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTY) {
292 					_ctx->frameNumber += 2;
293 				} else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTXY) {
294 					_ctx->frameNumber += 3;
295 				} else {
296 					// ANI_STOP, ANI_HIDE, ANI_HFLIP,
297 					// ANI_VFLIP, ANI_HVFLIP, default
298 					_ctx->frameNumber++;
299 				}
300 			}
301 		}
302 
303 		switch (FROM_32(pAni[_ctx->frameNumber].op)) {
304 		case ANI_END:
305 			// Stop this sample if repeating
306 			if (_ctx->sampleNumber && _ctx->bLooped)
307 				_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
308 			_ctx->bFinished = true;
309 			break;
310 
311 		case ANI_JUMP:
312 			_ctx->frameNumber++;
313 
314 			assert((int32)FROM_32(pAni[_ctx->frameNumber].op) < 0);
315 
316 			_ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op);
317 
318 			assert(_ctx->frameNumber >= 0);
319 			continue;
320 
321 		case ANI_STOP:
322 			// Stop this sample
323 			if (_ctx->sampleNumber)
324 				_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
325 			break;
326 
327 		case ANI_HIDE:
328 			// No op
329 			break;
330 
331 		case ANI_HFLIP:
332 		case ANI_VFLIP:
333 		case ANI_HVFLIP:
334 			_ctx->frameNumber++;
335 			continue;
336 
337 		case ANI_ADJUSTX:
338 		case ANI_ADJUSTY:
339 			_ctx->frameNumber += 2;
340 			continue;
341 
342 		case ANI_ADJUSTXY:
343 			_ctx->frameNumber += 3;
344 			continue;
345 
346 		default:
347 			// Stop this sample
348 			if (_ctx->sampleNumber)
349 				_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
350 
351 			_ctx->sampleNumber = FROM_32(pAni[_ctx->frameNumber++].op);
352 			if (_ctx->sampleNumber > 0)
353 				_ctx->bLooped = false;
354 			else {
355 				_ctx->sampleNumber = ~_ctx->sampleNumber;
356 				_ctx->bLooped = true;
357 			}
358 			x = (short)(FROM_32(pAni[_ctx->frameNumber].op) >> 16);
359 			y = (short)(FROM_32(pAni[_ctx->frameNumber].op) & 0xffff);
360 
361 			if (x == 0)
362 				x = -1;
363 
364 			_vm->_sound->playSample(_ctx->sampleNumber, 0, _ctx->bLooped, x, y, PRIORITY_SCRIPT,
365 					Audio::Mixer::kSFXSoundType);
366 
367 			break;
368 		}
369 
370 		CORO_SLEEP(_ctx->speed);
371 		_ctx->frameNumber++;
372 
373 		if (_ctx->reelActor && _vm->_actor->GetActorPresFilm(_ctx->reelActor) != hFilm) {
374 			// Stop this sample if repeating
375 			if (_ctx->sampleNumber && _ctx->bLooped)
376 				_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
377 
378 			_ctx->bFinished = true;
379 		}
380 
381 		if (myescEvent && myescEvent != GetEscEvents()) {
382 			// Stop this sample
383 			if (_ctx->sampleNumber)
384 				_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
385 
386 			_ctx->bFinished = true;
387 		}
388 	} while (!_ctx->bFinished && _ctx->myNum == g_soundReelNumbers[_ctx->myId]);
389 
390 	// De-register - if not been replaced
391 	if (_ctx->myNum == g_soundReelNumbers[_ctx->myId])
392 		DeRegisterSoundReel(hFilm, column);
393 
394 	CORO_END_CODE;
395 }
396 
ResSoundReel(CORO_PARAM,const void * param)397 static void ResSoundReel(CORO_PARAM, const void *param) {
398 	// get the stuff copied to process when it was created
399 	int i = *(const int *)param;
400 
401 	CORO_BEGIN_CONTEXT;
402 	CORO_END_CONTEXT(_ctx);
403 
404 	CORO_BEGIN_CODE(_ctx);
405 
406 	CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, g_soundReels[i].hFilm, g_soundReels[i].column,
407 		-1, 0, g_soundReels[i].actorCol));
408 
409 	CORO_KILL_SELF();
410 	CORO_END_CODE;
411 }
412 
SoundReelWaitCheck()413 static void SoundReelWaitCheck() {
414 	if (--g_soundReelWait == 0) {
415 		for (int i = 0; i < MAX_SOUNDREELS; i++) {
416 			if (g_soundReels[i].hFilm) {
417 				CoroScheduler.createProcess(PID_REEL, ResSoundReel, &i, sizeof(i));
418 			}
419 		}
420 	}
421 }
422 
423 /**
424  * - Don't bother if this reel is already playing for this actor.
425  * - If explicit co-ordinates, use these, If embedded co-ordinates,
426  * leave alone, otherwise use actor's current position.
427  * - Moving actors get hidden during this play, other actors get
428  * _ctx->replaced by this play.
429  * - Column 0 of a film gets its appropriate Z-position, slave columns
430  * get slightly bigger Z-positions, in column order.
431  * - Play proceeds until the script finishes, another reel starts up for
432  * this actor, or the actor gets killed.
433  * - If called from an splay(), moving actor's co-ordinates are updated
434  * after the play, any walk still in progress will go on from there.
435  */
t1PlayReel(CORO_PARAM,const PPINIT * ppi)436 static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) {
437 	CORO_BEGIN_CONTEXT;
438 		OBJECT	*pPlayObj;	// Object
439 		ANIM	thisAnim;	// Animation structure
440 
441 		bool	mActor;		// Gets set if this is a moving actor
442 		bool	lifeNoMatter;
443 		bool	replaced;
444 
445 		const FREEL *pfreel;	// The 'column' to play
446 		int		stepCount;
447 		int		frameCount;
448 		int		reelActor;
449 		PMOVER	pActor;
450 		int tmpX, tmpY;
451 	CORO_END_CONTEXT(_ctx);
452 
453 	CORO_BEGIN_CODE(_ctx);
454 
455 	const MULTI_INIT *pmi;		// MULTI_INIT structure
456 	bool	bNewMover;	// Gets set if a moving actor that isn't in scene yet
457 
458 	const FILM *pfilm;
459 
460 	_ctx->lifeNoMatter = false;
461 	_ctx->replaced = false;
462 	_ctx->pActor= nullptr;
463 	bNewMover = false;
464 
465 	pfilm = (const FILM *)_vm->_handle->LockMem(ppi->hFilm);
466 	_ctx->pfreel = &pfilm->reels[ppi->column];
467 
468 	// Get the MULTI_INIT structure
469 	pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(_ctx->pfreel->mobj));
470 
471 	// Save actor's ID
472 	_ctx->reelActor = (int32)FROM_32(pmi->mulID);
473 
474 	/**** New (experimental? bit 5/1/95 ****/
475 	if (!TinselV0 && !_vm->_actor->actorAlive(_ctx->reelActor))
476 		return;
477 	/**** Delete a bit down there if this stays ****/
478 
479 	_vm->_actor->UpdateActorEsc(_ctx->reelActor, ppi->escOn, ppi->myescEvent);
480 
481 	// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
482 	if (ppi->hFilm != _vm->_actor->GetActorLatestFilm(_ctx->reelActor)) {
483 		// This in not the last film scheduled for this actor
484 
485 		// It may be the last non-talk film though
486 		if (_vm->_actor->ActorIsTalking(_ctx->reelActor))
487 			_vm->_actor->SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
488 
489 		return;
490 	}
491 	if (_vm->_actor->ActorIsTalking(_ctx->reelActor)) {
492 		// Note: will delete this and there'll be no need to store the talk film!
493 		if (ppi->hFilm != _vm->_actor->GetActorTalkFilm(_ctx->reelActor)) {
494 			_vm->_actor->SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
495 			return;
496 		}
497 	} else {
498 		_vm->_actor->SetActorPlayFilm(_ctx->reelActor, ppi->hFilm);
499 	}
500 
501 	// If this reel is already playing for this actor, just forget it.
502 	if (_vm->_actor->actorReel(_ctx->reelActor) == _ctx->pfreel)
503 		return;
504 
505 	// Poke in the background palette
506 	PokeInPalette(FROM_32(pmi->hMulFrame));
507 
508 	// Set up and insert the multi-object
509 	_ctx->pPlayObj = MultiInitObject(pmi);
510 	if (!ppi->bTop)
511 		MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
512 	else
513 		MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
514 
515 	// If co-ordinates are specified, use specified.
516 	// Otherwise, use actor's position if there are not embedded co-ords.
517 	// Add this first test for nth columns with offsets
518 	// in plays with (x,y)
519 	_ctx->tmpX = ppi->x;
520 	_ctx->tmpY = ppi->y;
521 	if (ppi->column != 0 && (pmi->mulX || pmi->mulY)) {
522 	} else if (_ctx->tmpX != -1 || _ctx->tmpY != -1) {
523 		MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
524 	} else if (!pmi->mulX && !pmi->mulY) {
525 		_vm->_actor->GetActorPos(_ctx->reelActor, &_ctx->tmpX, &_ctx->tmpY);
526 		MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
527 	}
528 
529 	// If it's a moving actor, this hides the moving actor
530 	// used to do this only if (actorid == 0) - I don't know why
531 	_ctx->mActor = _vm->_actor->HideMovingActor(_ctx->reelActor, ppi->sf);
532 
533 	// If it's a moving actor, get its MOVER structure.
534 	// If it isn't in the scene yet, get its task running - using
535 	// Stand() - to prevent a glitch at the end of the play.
536 	if (_ctx->mActor) {
537 		_ctx->pActor = GetMover(_ctx->reelActor);
538 		if (!getMActorState(_ctx->pActor)) {
539 			CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, _ctx->reelActor, MAGICX, MAGICY, 0));
540 			bNewMover = true;
541 		}
542 	}
543 
544 	// Register the fact that we're playing this for this actor
545 	_vm->_actor->storeActorReel(_ctx->reelActor, _ctx->pfreel, ppi->hFilm, _ctx->pPlayObj, ppi->column, _ctx->tmpX, _ctx->tmpY);
546 
547 	/**** Will get rid of this if the above is kept ****/
548 	// We may be temporarily resuscitating a dead actor
549 	if (ppi->actorid == 0 && !_vm->_actor->actorAlive(_ctx->reelActor))
550 		_ctx->lifeNoMatter = true;
551 
552 	InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj,  FROM_32(_ctx->pfreel->script), ppi->speed);
553 
554 	// If first column, set Z position as per
555 	// Otherwise, column 0's + column number
556 	// N.B. It HAS been ensured that the first column gets here first
557 	if (ppi->z != 0) {
558 		MultiSetZPosition(_ctx->pPlayObj, ppi->z);
559 		_vm->_actor->StoreActorZpos(_ctx->reelActor, ppi->z);
560 	} else if (ppi->bTop) {
561 		if (ppi->column == 0) {
562 			firstColZ = Z_TOPPLAY + _vm->_actor->actorMaskType(_ctx->reelActor);
563 			MultiSetZPosition(_ctx->pPlayObj, firstColZ);
564 			_vm->_actor->StoreActorZpos(_ctx->reelActor, firstColZ);
565 		} else {
566 			MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
567 			_vm->_actor->StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
568 		}
569 	} else if (ppi->column == 0) {
570 		if (_ctx->mActor && !bNewMover) {
571 			// If no path, just use first path in the scene
572 			if (_ctx->pActor->hCpath == NOPOLY)
573 				fColZfactor = GetPolyZfactor(FirstPathPoly());
574 			else
575 				fColZfactor = GetPolyZfactor(_ctx->pActor->hCpath);
576 			firstColZ = _vm->_actor->AsetZPos(_ctx->pPlayObj, MultiLowest(_ctx->pPlayObj), fColZfactor);
577 		} else {
578 			switch (_vm->_actor->actorMaskType(_ctx->reelActor)) {
579 			case ACT_DEFAULT:
580 				fColZfactor = 0;
581 				firstColZ = 2;
582 				MultiSetZPosition(_ctx->pPlayObj, firstColZ);
583 				break;
584 			case ACT_MASK:
585 				fColZfactor = 0;
586 				firstColZ = MultiLowest(_ctx->pPlayObj);
587 				MultiSetZPosition(_ctx->pPlayObj, firstColZ);
588 				break;
589 			case ACT_ALWAYS:
590 				fColZfactor = 10;
591 				firstColZ = 10000;
592 				MultiSetZPosition(_ctx->pPlayObj, firstColZ);
593 				break;
594 			default:
595 				fColZfactor = _vm->_actor->actorMaskType(_ctx->reelActor);
596 				firstColZ = _vm->_actor->AsetZPos(_ctx->pPlayObj, MultiLowest(_ctx->pPlayObj), fColZfactor);
597 				if (firstColZ < 2) {
598 					// This is an experiment!
599 					firstColZ = 2;
600 					MultiSetZPosition(_ctx->pPlayObj, firstColZ);
601 				}
602 				break;
603 			}
604 		}
605 		_vm->_actor->StoreActorZpos(_ctx->reelActor, firstColZ);
606 	} else {
607 		if (NoNameFunc(_ctx->reelActor, bNewMover) > fColZfactor) {
608 			fColZfactor = NoNameFunc(_ctx->reelActor, bNewMover);
609 			firstColZ = fColZfactor << 10;
610 		}
611 		MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
612 		_vm->_actor->StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
613 	}
614 
615 	/*
616 	 * Play until the script finishes,
617 	 * another reel starts up for this actor,
618 	 * or the actor gets killed.
619 	 */
620 	_ctx->stepCount = 0;
621 	_ctx->frameCount = 0;
622 	do {
623 		if (_ctx->stepCount++ == 0) {
624 			_ctx->frameCount++;
625 			_vm->_actor->StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
626 		}
627 		if (_ctx->stepCount == ppi->speed)
628 			_ctx->stepCount = 0;
629 
630 		if (StepAnimScript(&_ctx->thisAnim) == ScriptFinished)
631 			break;
632 
633 		int x, y;
634 		GetAniPosition(_ctx->pPlayObj, &x, &y);
635 		_vm->_actor->StoreActorPos(_ctx->reelActor, x, y);
636 
637 		CORO_SLEEP(1);
638 
639 		if (_vm->_actor->actorReel(_ctx->reelActor) != _ctx->pfreel) {
640 			_ctx->replaced = true;
641 			break;
642 		}
643 
644 		if (_vm->_actor->ActorEsc(_ctx->reelActor) && _vm->_actor->ActorEev(_ctx->reelActor) != GetEscEvents())
645 			break;
646 
647 	} while (_ctx->lifeNoMatter || _vm->_actor->actorAlive(_ctx->reelActor));
648 
649 	// Register the fact that we're NOT playing this for this actor
650 	if (_vm->_actor->actorReel(_ctx->reelActor) == _ctx->pfreel)
651 		_vm->_actor->storeActorReel(_ctx->reelActor, NULL, 0, NULL, 0, 0, 0);
652 
653 	// Ditch the object
654 	if (!ppi->bTop)
655 		MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
656 	else
657 		MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
658 
659 	if (_ctx->mActor) {
660 		if (!_ctx->replaced)
661 			_vm->_actor->unHideMovingActor(_ctx->reelActor); // Restore moving actor
662 
663 		// Update it's co-ordinates if this is an splay()
664 		if (ppi->splay)
665 			_vm->_actor->restoreMovement(_ctx->reelActor);
666 	}
667 	CORO_END_CODE;
668 }
669 
670 /**
671  * - Don't bother if this reel is already playing for this actor.
672  * - If explicit co-ordinates, use these, If embedded co-ordinates,
673  * leave alone, otherwise use actor's current position.
674  * - Moving actors get hidden during this play, other actors get
675  * replaced by this play.
676  * - Column 0 of a film gets its appropriate Z-position, slave columns
677  * get slightly bigger Z-positions, in column order.
678  * - Play proceeds until the script finishes, another reel starts up for
679  * this actor, or the actor gets killed.
680  * - If called from an splay(), moving actor's co-ordinates are updated
681  * after the play, any walk still in progress will go on from there.
682  * @param x				Co-ordinates from the play(), set to (-1, -1) if none
683  * @param y				Co-ordinates from the play(), set to (-1, -1) if none
684  * @param bRestore		Normally False, set if from restore
685  * @param speed			Film speed
686  * @param hFilm			The 'film'
687  * @param column		Column number, first column = 0
688  */
t2PlayReel(CORO_PARAM,int x,int y,bool bRestore,int speed,SCNHANDLE hFilm,int column,int myescEvent,bool bTop,OBJECT ** playfield)689 static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHANDLE hFilm, int column, int myescEvent, bool bTop, OBJECT** playfield) {
690 	CORO_BEGIN_CONTEXT;
691 		bool bReplaced;
692 		bool bGotHidden;
693 		int	stepCount;
694 		int	frameCount;
695 		bool bEscapedAlready;
696 		bool	bPrinciple;	// true if this is the first column in a film for one actor
697 		bool	bRelative;	// true if relative specified in script
698 
699 		FREEL		*pFreel;
700 		MULTI_INIT	*pmi;	// MULTI_INIT structure
701 		POBJECT		pPlayObj;	// Object
702 		ANIM		thisAnim;	// Animation structure
703 
704 		int	reelActor;			// Which actor this reel belongs to
705 		PMOVER	pMover;			// set if it's a moving actor
706 		bool	bNewMover;		// Gets set if a moving actor that isn't in scene yet
707 
708 		int	filmNumber;
709 		int	myZ;				// Remember for hide/unhide
710 	CORO_END_CONTEXT(_ctx);
711 
712 	CORO_BEGIN_CODE(_ctx);
713 
714 	_ctx->bReplaced = false;
715 	_ctx->bGotHidden = false;
716 	_ctx->stepCount = 0;
717 	_ctx->frameCount = 0;
718 
719 	_ctx->bEscapedAlready = false;
720 
721 	// Get the reel and MULTI_INIT structure
722 	_ctx->pFreel = GetReel(hFilm, column);
723 	_ctx->pmi = (MULTI_INIT *)_vm->_handle->LockMem(FROM_32(_ctx->pFreel->mobj));
724 
725 	if ((int32)FROM_32(_ctx->pmi->mulID) == -2) {
726 		CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, hFilm, column, speed, myescEvent,
727 			FROM_32(_ctx->pmi->otherFlags) & OTH_RELATEDACTOR));
728 		return;
729 	}
730 
731 	// Save actor's ID
732 	_ctx->reelActor = FROM_32(_ctx->pmi->mulID);
733 
734 	_vm->_actor->UpdateActorEsc(_ctx->reelActor, myescEvent);
735 
736 	// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
737 	if (hFilm != _vm->_actor->GetActorLatestFilm(_ctx->reelActor)) {
738 		// This in not the last film scheduled for this actor
739 
740 		// It may be the last non-talk film though
741 		if (_vm->_actor->ActorIsTalking(_ctx->reelActor))
742 			_vm->_actor->SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
743 
744 		return;
745 	}
746 	if (_vm->_actor->ActorIsTalking(_ctx->reelActor)) {
747 		// Note: will delete this and there'll be no need to store the talk film!
748 		if (hFilm != _vm->_actor->GetActorTalkFilm(_ctx->reelActor)) {
749 			_vm->_actor->SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
750 			return;
751 		}
752 	} else {
753 		_vm->_actor->SetActorPlayFilm(_ctx->reelActor, hFilm);
754 	}
755 
756 	// Register the film for this actor
757 	if (hFilm != _vm->_actor->GetActorPresFilm(_ctx->reelActor)) {
758 		_ctx->bPrinciple = true;
759 		_vm->_actor->StoreActorPresFilm(_ctx->reelActor, hFilm, x, y);
760 	} else {
761 		_ctx->bPrinciple = false;
762 
763 		// If this reel is already playing for this actor, just forget it.
764 		if (_vm->_actor->ActorReelPlaying(_ctx->reelActor, column))
765 			return;
766 	}
767 
768 	/*
769 	 * Insert the object
770 	 */
771 	if (!TinselV3) {
772 		// Poke in the background palette
773 		PokeInPalette(_ctx->pmi);
774 	}
775 
776 	// Set ghost bit if wanted
777 	if (_vm->_actor->ActorIsGhost(_ctx->reelActor)) {
778 		assert(FROM_32(_ctx->pmi->mulFlags) == DMA_WNZ || FROM_32(_ctx->pmi->mulFlags) == (DMA_WNZ | DMA_GHOST));
779 		_ctx->pmi->mulFlags = TO_32(FROM_32(_ctx->pmi->mulFlags) | DMA_GHOST);
780 	}
781 
782 	// Set up and insert the multi-object
783 	_ctx->pPlayObj = MultiInitObject(_ctx->pmi);
784 	if (TinselV3) {
785 		MultiInsertObject(playfield, _ctx->pPlayObj);
786 	} else {
787 		if (!bTop)
788 			MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
789 		else
790 			MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
791 	}
792 
793 	/*
794 	 * More action for moving actors
795 	*/
796 	_ctx->pMover = GetMover(_ctx->reelActor);
797 	if (_ctx->pMover != NULL) {
798 		HideMover(_ctx->pMover);
799 
800 		if (!MoverIs(_ctx->pMover)) {
801 			// Used to do a Stand here to prevent glitches
802 
803 			_ctx->bNewMover = true;
804 		} else
805 			_ctx->bNewMover = false;
806 	}
807 
808 	// Register the reel for this actor
809 	_vm->_actor->StoreActorReel(_ctx->reelActor, column, _ctx->pPlayObj);
810 
811 	_ctx->filmNumber = _vm->_actor->GetActorFilmNumber(_ctx->reelActor);
812 
813 	/*
814 	 * Sort out x and y
815 	 */
816 	assert( ((FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE))
817 		|| ((FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE)) );
818 
819 	_ctx->bRelative = FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE;
820 
821 	if (_ctx->bRelative) {
822 		// Use actor's position. If (x, y) specified, move the actor.
823 		if (x == -1 && y == -1)
824 			_vm->_actor->GetActorPos(_ctx->reelActor, &x, &y);
825 		else
826 			_vm->_actor->StoreActorPos(_ctx->reelActor, x, y);
827 	} else if (x == -1 && y == -1)
828 		x = y = 0;		// Use (0,0) if no specified
829 
830 	// Add embedded co-ords
831 	MultiSetAniXY(_ctx->pPlayObj, x + FROM_32(_ctx->pmi->mulX), y + FROM_32(_ctx->pmi->mulY));
832 
833 	/*
834 	 * Sort out z
835 	 */
836 	if (bRestore) {
837 		_ctx->myZ = _vm->_actor->GetActorZpos(_ctx->reelActor, column);
838 
839 		SoundReelWaitCheck();
840 	} else {
841 		// N.B. It HAS been ensured that the first column gets here first
842 
843 		if ((int32)FROM_32(_ctx->pmi->mulZ) != -1) {
844 			// Z override in script
845 
846 			baseZfact = FROM_32(_ctx->pmi->mulZ);
847 			baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
848 			if (bTop)
849 				baseZposn += Z_TOPPLAY;
850 		} else if (column == 0
851 				|| GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover) > baseZfact) {
852 			// Subsequent columns are based on this one
853 
854 			baseZfact = GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover);
855 			baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
856 			if (bTop)
857 				baseZposn += Z_TOPPLAY;
858 		}
859 		_ctx->myZ = baseZposn + column;
860 	}
861 	MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
862 	_vm->_actor->StoreActorZpos(_ctx->reelActor, _ctx->myZ, column);
863 
864 	/*
865 	 * Play until the script finishes,
866 	 * another reel starts up for this actor,
867 	 * or the actor gets killed.
868 	 */
869 	InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_32(_ctx->pFreel->script), speed);
870 
871 	if (bRestore || (_vm->_actor->ActorEsc(_ctx->reelActor) == true &&
872 	                 _vm->_actor->ActorEev(_ctx->reelActor) != GetEscEvents())) {
873 		// From restore, step to jump or end
874 		SkipFrames(&_ctx->thisAnim, -1);
875 	}
876 
877 	for (;;) {
878 		if (_ctx->stepCount++ == 0) {
879 			_ctx->frameCount++;
880 			_vm->_actor->StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
881 		}
882 		if (_ctx->stepCount == speed)
883 			_ctx->stepCount = 0;
884 
885 		if (_ctx->bPrinciple && AboutToJumpOrEnd(&_ctx->thisAnim))
886 			_vm->_actor->IncLoopCount(_ctx->reelActor);
887 
888 		if (StepAnimScript(&_ctx->thisAnim) == ScriptFinished)
889 			break;
890 
891 		if (_ctx->bRelative) {
892 			GetAniPosition(_ctx->pPlayObj, &x, &y);
893 			_vm->_actor->StoreActorPos(_ctx->reelActor, x, y);
894 		}
895 
896 		if (_ctx->bGotHidden) {
897 			if (!_vm->_actor->ActorHidden(_ctx->reelActor)) {
898 				MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
899 				_ctx->bGotHidden = false;
900 			}
901 		} else {
902 			if (_vm->_actor->ActorHidden(_ctx->reelActor)) {
903 				MultiSetZPosition(_ctx->pPlayObj, -1);
904 				_ctx->bGotHidden = true;
905 			}
906 		}
907 
908 		CORO_SLEEP(1);
909 
910 		if (_vm->_actor->GetActorFilmNumber(_ctx->reelActor) != _ctx->filmNumber) {
911 			_ctx->bReplaced = true;
912 			break;
913 		}
914 
915 		if (_vm->_actor->ActorEsc(_ctx->reelActor) == true && _vm->_actor->ActorEev(_ctx->reelActor) != GetEscEvents()) {
916 			if (!_ctx->bEscapedAlready) {
917 				SkipFrames(&_ctx->thisAnim, -1);
918 				_ctx->bEscapedAlready = true;
919 			}
920 
921 //WHY???		UpdateActorEsc(reelActor, GetEscEvents());
922 // The above line of code, not commented out would fix the coffee pot flash
923 // but why was it commented out?
924 // The extra boolean is used instead, 'cos it's release week and I want to play it safe!
925 		}
926 	}
927 
928 	// Register the fact that we're NOT playing this for this actor
929 	_vm->_actor->NotPlayingReel(_ctx->reelActor, _ctx->filmNumber, column);
930 
931 	// Ditch the object
932 	if (TinselV3) {
933 		MultiDeleteObject(playfield, _ctx->pPlayObj);
934 	} else {
935 		if (!bTop) {
936 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
937 		} else {
938 			MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
939 		}
940 	}
941 
942 	// Restore moving actor is nessesary
943 	if (_ctx->pMover != NULL && _ctx->bPrinciple && !_ctx->bReplaced)
944 		UnHideMover(_ctx->pMover);
945 
946 	CORO_END_CODE;
947 }
948 
949 /**
950  * Run all animations that comprise the play film.
951  */
PlayProcess(CORO_PARAM,const void * param)952 static void PlayProcess(CORO_PARAM, const void *param) {
953 	CORO_BEGIN_CONTEXT;
954 	CORO_END_CONTEXT(_ctx);
955 
956 	const PPINIT *ppi = (const PPINIT *)param;
957 	CORO_BEGIN_CODE(_ctx);
958 
959 	if (TinselV2)
960 		CORO_INVOKE_ARGS(t2PlayReel, (CORO_SUBCTX, ppi->x, ppi->y, ppi->bRestore, ppi->speed,
961 			ppi->hFilm, ppi->column, ppi->myescEvent, ppi->bTop, ppi->playfield));
962 	else
963 		CORO_INVOKE_1(t1PlayReel, ppi);
964 
965 	CORO_END_CODE;
966 }
967 
968 // *******************************************************
969 
970 
971 // To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
NewestFilm(SCNHANDLE film,const FREEL * reel)972 void NewestFilm(SCNHANDLE film, const FREEL *reel) {
973 	const MULTI_INIT *pmi;		// MULTI_INIT structure
974 
975 	// Get the MULTI_INIT structure
976 	pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(reel->mobj));
977 
978 	if (!TinselV2 || ((int32)FROM_32(pmi->mulID) != -2))
979 		_vm->_actor->SetActorLatestFilm((int32)FROM_32(pmi->mulID), film);
980 }
981 
982 // *******************************************************
983 
984 /**
985  * Start up a play process for each column in a film.
986  *
987  * NOTE: The processes are started in reverse order so that the first
988  *   column's process kicks in first.
989  */
PlayFilm(CORO_PARAM,SCNHANDLE hFilm,int x,int y,int actorid,bool splay,bool sfact,bool escOn,int myescEvent,bool bTop,OBJECT ** playfield)990 void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield) {
991 	assert(hFilm != 0); // Trying to play NULL film
992 	const FILM *pFilm;
993 
994 	CORO_BEGIN_CONTEXT;
995 	CORO_END_CONTEXT(_ctx);
996 
997 	CORO_BEGIN_CODE(_ctx);
998 
999 	pFilm = (const FILM *)_vm->_handle->LockMem(hFilm);
1000 	PPINIT ppi;
1001 
1002 	// Now allowed empty films!
1003 	if (pFilm->numreels == 0)
1004 		return;                 // Nothing to do!
1005 
1006 	ppi.hFilm = hFilm;
1007 	ppi.x = x;
1008 	ppi.y = y;
1009 	ppi.z = 0;
1010 	ppi.bRestore = false;
1011 	ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate));
1012 	ppi.actorid = actorid;
1013 	ppi.splay = splay;
1014 	ppi.bTop = bTop;
1015 	ppi.sf = sfact;
1016 	ppi.escOn = escOn;
1017 	ppi.myescEvent = myescEvent;
1018 	ppi.playfield = playfield;
1019 
1020 	// Start display process for each reel in the film
1021 	for (int i = FROM_32(pFilm->numreels) - 1; i >= 0; i--) {
1022 		NewestFilm(hFilm, &pFilm->reels[i]);
1023 
1024 		ppi.column = i;
1025 		CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(PPINIT));
1026 	}
1027 
1028 	if (TinselV2) {
1029 		// Let it all kick in and position this process
1030 		// down the process list from the playing process(es)
1031 		// This ensures something
1032 		CORO_GIVE_WAY;
1033 
1034 		if (myescEvent && myescEvent != GetEscEvents())
1035 			CoroScheduler.rescheduleAll();
1036 	}
1037 
1038 	CORO_END_CODE;
1039 }
1040 
PlayFilm(CORO_PARAM,SCNHANDLE hFilm,int x,int y,int myescEvent,bool bTop,OBJECT ** playfield)1041 void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop, OBJECT** playfield) {
1042 	PlayFilm(coroParam, hFilm, x, y, 0, false, false, false, myescEvent, bTop, playfield);
1043 }
1044 
1045 /**
1046  * Start up a play process for each slave column in a film.
1047  * Play the first column directly from the parent process.
1048  */
PlayFilmc(CORO_PARAM,SCNHANDLE hFilm,int x,int y,int actorid,bool splay,bool sfact,bool escOn,int myescEvent,bool bTop,OBJECT ** playfield)1049 void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn, int myescEvent, bool bTop, OBJECT** playfield) {
1050 	CORO_BEGIN_CONTEXT;
1051 		PPINIT ppi;
1052 		int i;
1053 		int loopCount;
1054 	CORO_END_CONTEXT(_ctx);
1055 
1056 	CORO_BEGIN_CODE(_ctx);
1057 
1058 	assert(hFilm != 0); // Trying to play NULL film
1059 	const FILM *pFilm;
1060 
1061 	pFilm = (const FILM *)_vm->_handle->LockMem(hFilm);
1062 
1063 	// Now allowed empty films!
1064 	if (pFilm->numreels == 0)
1065 		return;                 //  Already played to completion!
1066 
1067 	_ctx->ppi.hFilm = hFilm;
1068 	_ctx->ppi.x = x;
1069 	_ctx->ppi.y = y;
1070 	_ctx->ppi.z = 0;
1071 	_ctx->ppi.bRestore = false;
1072 	_ctx->ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate));
1073 	_ctx->ppi.actorid = actorid;
1074 	_ctx->ppi.splay = splay;
1075 	_ctx->ppi.bTop = bTop;
1076 	_ctx->ppi.sf = sfact;
1077 	_ctx->ppi.escOn = escOn;
1078 	_ctx->ppi.myescEvent = myescEvent;
1079 	_ctx->ppi.playfield = playfield;
1080 
1081 	// Start display process for each secondary reel in the film in Tinsel 1,
1082 	// or all of them in Tinsel 2
1083 	for (int i = FROM_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) {
1084 		NewestFilm(hFilm, &pFilm->reels[i]);
1085 
1086 		_ctx->ppi.column = i;
1087 		CoroScheduler.createProcess(PID_REEL, PlayProcess, &_ctx->ppi, sizeof(PPINIT));
1088 	}
1089 
1090 	if (TinselV2) {
1091 		// Let it all kick in and position this 'waiting' process
1092 		// down the process list from the playing process(es)
1093 		// This ensures immediate return when the reel finishes
1094 		CORO_GIVE_WAY;
1095 
1096 		_ctx->i = ExtractActor(hFilm);
1097 		_ctx->loopCount = _vm->_actor->GetLoopCount(_ctx->i);
1098 
1099 		// Wait until film changes or loop count increases
1100 		while (_vm->_actor->GetActorPresFilm(_ctx->i) == hFilm && _vm->_actor->GetLoopCount(_ctx->i) == _ctx->loopCount) {
1101 			if (myescEvent && myescEvent != GetEscEvents()) {
1102 				CoroScheduler.rescheduleAll();
1103 				break;
1104 			}
1105 
1106 			CORO_SLEEP(1);
1107 		}
1108 	} else {
1109 		// For Tinsel 1, launch the primary reel
1110 		NewestFilm(hFilm, &pFilm->reels[0]);
1111 
1112 		_ctx->ppi.column = 0;
1113 		CORO_INVOKE_1(t1PlayReel, &_ctx->ppi);
1114 	}
1115 
1116 	CORO_END_CODE;
1117 }
1118 
1119 /**
1120  * Start up a play process for a particular column in a film.
1121  *
1122  * NOTE: This is specifically for actors during a Tinsel 1 restore scene.
1123  */
RestoreActorReels(SCNHANDLE hFilm,short reelnum,short z,int x,int y)1124 void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y) {
1125 	assert(!TinselV2);
1126 	const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(hFilm);
1127 	PPINIT ppi;
1128 
1129 	ppi.hFilm = hFilm;
1130 	ppi.x = x;
1131 	ppi.y = y;
1132 	ppi.z = z;
1133 	ppi.speed = (ONE_SECOND / FROM_32(pfilm->frate));
1134 	ppi.actorid = 0;
1135 	ppi.splay = false;
1136 	ppi.bTop = false;
1137 	ppi.bRestore = true;
1138 	ppi.sf = 0;
1139 	ppi.column = reelnum;
1140 	ppi.myescEvent = 0;
1141 
1142 	ppi.escOn = false;
1143 	ppi.myescEvent = GetEscEvents();
1144 
1145 	assert(pfilm->numreels);
1146 
1147 	NewestFilm(hFilm, &pfilm->reels[reelnum]);
1148 
1149 	// Start display process for the reel
1150 	CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
1151 }
1152 
1153 /**
1154  * Start up a play process for a particular column in a film.
1155  *
1156  * NOTE: This is specifically for actors during a Tinsel 2 restore scene.
1157  */
RestoreActorReels(SCNHANDLE hFilm,int actor,int x,int y)1158 void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y) {
1159 	assert(TinselV2);
1160 	FILM *pFilm = (FILM *)_vm->_handle->LockMem(hFilm);
1161 	PPINIT ppi;
1162 
1163 	int i;
1164 	FREEL *pFreel;
1165 	PMULTI_INIT	pmi;		// MULTI_INIT structure
1166 
1167 	ppi.hFilm = hFilm;
1168 	ppi.x = (short)x;
1169 	ppi.y = (short)y;
1170 	ppi.bRestore = true;
1171 	ppi.speed = (short)(ONE_SECOND/FROM_32(pFilm->frate));
1172 	ppi.bTop = false;
1173 	ppi.myescEvent = 0;
1174 
1175 	// Search backwards for now as later column will be the one
1176 	for (i = (int)FROM_32(pFilm->numreels) - 1; i >= 0; i--) {
1177 		pFreel = &pFilm->reels[i];
1178 		pmi = (PMULTI_INIT)_vm->_handle->LockMem(FROM_32(pFreel->mobj));
1179 		if ((int32)FROM_32(pmi->mulID) == actor) {
1180 			ppi.column = (short)i;
1181 			NewestFilm(hFilm, &pFilm->reels[i]);
1182 
1183 			// Start display process for the reel
1184 			CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
1185 
1186 			g_soundReelWait++;
1187 		}
1188 	}
1189 }
1190 
1191 /**
1192  * Get the actor id from a film (column 0)
1193  */
ExtractActor(SCNHANDLE hFilm)1194 int ExtractActor(SCNHANDLE hFilm) {
1195 	const FILM *pFilm = (const FILM *)_vm->_handle->LockMem(hFilm);
1196 	const FREEL *pReel = &pFilm->reels[0];
1197 	const MULTI_INIT *pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pReel->mobj));
1198 	return (int)FROM_32(pmi->mulID);
1199 }
1200 
1201 } // End of namespace Tinsel
1202