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