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