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