mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 14:50:17 +00:00
1181 lines
32 KiB
C++
1181 lines
32 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Plays films within a scene, takes into account the actor in each 'column'. |
|
|
*/
|
|
|
|
#include "common/coroutines.h"
|
|
#include "tinsel/actors.h"
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/dw.h"
|
|
#include "tinsel/film.h"
|
|
#include "tinsel/handle.h"
|
|
#include "tinsel/multiobj.h"
|
|
#include "tinsel/object.h"
|
|
#include "tinsel/pid.h"
|
|
#include "tinsel/play.h"
|
|
#include "tinsel/polygons.h"
|
|
#include "tinsel/rince.h"
|
|
#include "tinsel/sched.h"
|
|
#include "tinsel/scn.h"
|
|
#include "tinsel/sound.h"
|
|
#include "tinsel/timers.h"
|
|
#include "tinsel/tinlib.h" // Stand()
|
|
|
|
namespace Tinsel {
|
|
|
|
struct PPINIT {
|
|
SCNHANDLE hFilm; // The 'film'
|
|
int16 x; // } Co-ordinates from the play()
|
|
int16 y; // } - set to (-1, -1) if none.
|
|
int16 z; // normally 0, set if from restore
|
|
int16 speed; // Film speed
|
|
int16 actorid; // Set if called from an actor code block
|
|
uint8 splay; // Set if called from splay()
|
|
uint8 bTop; // Set if called from topplay()
|
|
uint8 bRestore;
|
|
int16 sf; // SlowFactor - only used for moving actors
|
|
int16 column; // Column number, first column = 0
|
|
|
|
uint8 escOn;
|
|
int32 myescEvent;
|
|
};
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
// FIXME: Avoid non-const global vars
|
|
|
|
static SOUNDREELS g_soundReels[MAX_SOUNDREELS];
|
|
static int g_soundReelNumbers[MAX_SOUNDREELS];
|
|
|
|
static int g_soundReelWait;
|
|
|
|
//-------------------- METHODS ----------------------
|
|
|
|
/**
|
|
* Poke the background palette into an image.
|
|
*/
|
|
static void PokeInPalette(SCNHANDLE hMulFrame) {
|
|
const FRAME *pFrame; // Pointer to frame
|
|
IMAGE *pim; // Pointer to image
|
|
|
|
// Could be an empty column
|
|
if (hMulFrame) {
|
|
pFrame = (const FRAME *)LockMem(hMulFrame);
|
|
|
|
// get pointer to image
|
|
pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image
|
|
|
|
pim->hImgPal = TO_32(BgPal());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Poke the background palette into an image.
|
|
*/
|
|
void PokeInPalette(const MULTI_INIT *pmi) {
|
|
FRAME *pFrame; // Pointer to frame
|
|
IMAGE *pim; // Pointer to image
|
|
|
|
// Could be an empty column
|
|
if (pmi->hMulFrame) {
|
|
pFrame = (FRAME *)LockMem(FROM_32(pmi->hMulFrame));
|
|
|
|
// get pointer to image
|
|
pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image
|
|
|
|
pim->hImgPal = TO_32(BgPal());
|
|
}
|
|
}
|
|
|
|
int32 NoNameFunc(int actorID, bool bNewMover) {
|
|
PMOVER pActor;
|
|
int32 retval;
|
|
|
|
pActor = GetMover(actorID);
|
|
|
|
if (pActor != NULL && !bNewMover) {
|
|
// If no path, just use first path in the scene
|
|
if (pActor->hCpath == NOPOLY)
|
|
retval = GetPolyZfactor(FirstPathPoly());
|
|
else
|
|
retval = GetPolyZfactor(pActor->hCpath);
|
|
} else {
|
|
switch (actorMaskType(actorID)) {
|
|
case ACT_DEFAULT:
|
|
retval = 0;
|
|
break;
|
|
case ACT_MASK:
|
|
retval = 0;
|
|
break;
|
|
case ACT_ALWAYS:
|
|
retval = 10;
|
|
break;
|
|
default:
|
|
retval = actorMaskType(actorID);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
static FREEL *GetReel(SCNHANDLE hFilm, int column) {
|
|
FILM *pFilm = (FILM *)LockMem(hFilm);
|
|
|
|
return &pFilm->reels[column];
|
|
}
|
|
|
|
static int RegisterSoundReel(SCNHANDLE hFilm, int column, int actorCol) {
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_SOUNDREELS; i++) {
|
|
// Should assert this doesn't happen, but let's be tolerant
|
|
if (g_soundReels[i].hFilm == hFilm && g_soundReels[i].column == column)
|
|
break;
|
|
|
|
if (!g_soundReels[i].hFilm) {
|
|
g_soundReels[i].hFilm = hFilm;
|
|
g_soundReels[i].column = column;
|
|
g_soundReels[i].actorCol = actorCol;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_soundReelNumbers[i]++;
|
|
return i;
|
|
}
|
|
|
|
void NoSoundReels() {
|
|
memset(g_soundReels, 0, sizeof(g_soundReels));
|
|
g_soundReelWait = 0;
|
|
}
|
|
|
|
static void DeRegisterSoundReel(SCNHANDLE hFilm, int column) {
|
|
for (int i = 0; i < MAX_SOUNDREELS; i++) {
|
|
// Should assert this doesn't happen, but let's be tolerant
|
|
if (g_soundReels[i].hFilm == hFilm && g_soundReels[i].column == column) {
|
|
g_soundReels[i].hFilm = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveSoundReels(PSOUNDREELS psr) {
|
|
for (int i = 0; i < MAX_SOUNDREELS; i++) {
|
|
if (IsCdPlayHandle(g_soundReels[i].hFilm))
|
|
g_soundReels[i].hFilm = 0;
|
|
}
|
|
|
|
memcpy(psr, g_soundReels, sizeof(g_soundReels));
|
|
}
|
|
|
|
void RestoreSoundReels(PSOUNDREELS psr) {
|
|
memcpy(g_soundReels, psr, sizeof(g_soundReels));
|
|
}
|
|
|
|
static uint32 GetZfactor(int actorID, PMOVER pMover, bool bNewMover) {
|
|
if (pMover != NULL && bNewMover == false) {
|
|
// If no path, just use first path in the scene
|
|
if (pMover->hCpath == NOPOLY)
|
|
return GetPolyZfactor(FirstPathPoly());
|
|
else
|
|
return GetPolyZfactor(pMover->hCpath);
|
|
} else {
|
|
return GetActorZfactor(actorID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles reels with sound id.
|
|
* @param hFilm The 'film'
|
|
* @param column Column number, first column = 0
|
|
* @param speed Film speed
|
|
*/
|
|
static void SoundReel(CORO_PARAM, SCNHANDLE hFilm, int column, int speed,
|
|
int myescEvent, int actorCol) {
|
|
FILM *pFilm;
|
|
FREEL *pReel;
|
|
ANI_SCRIPT *pAni;
|
|
|
|
short x, y;
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
int myId;
|
|
int myNum;
|
|
int frameNumber;
|
|
int speed;
|
|
int sampleNumber;
|
|
bool bFinished;
|
|
bool bLooped;
|
|
int reelActor;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (actorCol) {
|
|
PMULTI_INIT pmi; // MULTI_INIT structure
|
|
|
|
pReel = GetReel(hFilm, actorCol - 1);
|
|
pmi = (PMULTI_INIT) LockMem(FROM_32(pReel->mobj));
|
|
_ctx->reelActor = (int32)FROM_32(pmi->mulID);
|
|
} else
|
|
_ctx->reelActor = 0;
|
|
|
|
_ctx->frameNumber = 0;
|
|
_ctx->speed = speed;
|
|
_ctx->sampleNumber = 0;
|
|
_ctx->bFinished = false;
|
|
_ctx->bLooped = false;
|
|
_ctx->myId = RegisterSoundReel(hFilm, column, actorCol);
|
|
_ctx->myNum = g_soundReelNumbers[_ctx->myId];
|
|
|
|
do {
|
|
pFilm = (FILM *)LockMem(hFilm);
|
|
pReel = &pFilm->reels[column];
|
|
|
|
pAni = (ANI_SCRIPT *)LockMem(FROM_32(pReel->script));
|
|
|
|
if (_ctx->speed == -1) {
|
|
_ctx->speed = (ONE_SECOND/FROM_32(pFilm->frate));
|
|
|
|
// Restored reel
|
|
for (;;) {
|
|
if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_END)
|
|
break;
|
|
else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_JUMP) {
|
|
_ctx->frameNumber++;
|
|
_ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op);
|
|
break;
|
|
}
|
|
// Could check for the other stuff here
|
|
// but they really dont happen
|
|
// OH YES THEY DO
|
|
else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTX
|
|
|| FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTY) {
|
|
_ctx->frameNumber += 2;
|
|
} else if (FROM_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTXY) {
|
|
_ctx->frameNumber += 3;
|
|
} else {
|
|
// ANI_STOP, ANI_HIDE, ANI_HFLIP,
|
|
// ANI_VFLIP, ANI_HVFLIP, default
|
|
_ctx->frameNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (FROM_32(pAni[_ctx->frameNumber].op)) {
|
|
case ANI_END:
|
|
// Stop this sample if repeating
|
|
if (_ctx->sampleNumber && _ctx->bLooped)
|
|
_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
|
|
_ctx->bFinished = true;
|
|
break;
|
|
|
|
case ANI_JUMP:
|
|
_ctx->frameNumber++;
|
|
|
|
assert((int32)FROM_32(pAni[_ctx->frameNumber].op) < 0);
|
|
|
|
_ctx->frameNumber += FROM_32(pAni[_ctx->frameNumber].op);
|
|
|
|
assert(_ctx->frameNumber >= 0);
|
|
continue;
|
|
|
|
case ANI_STOP:
|
|
// Stop this sample
|
|
if (_ctx->sampleNumber)
|
|
_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
|
|
break;
|
|
|
|
case ANI_HIDE:
|
|
// No op
|
|
break;
|
|
|
|
case ANI_HFLIP:
|
|
case ANI_VFLIP:
|
|
case ANI_HVFLIP:
|
|
_ctx->frameNumber++;
|
|
continue;
|
|
|
|
case ANI_ADJUSTX:
|
|
case ANI_ADJUSTY:
|
|
_ctx->frameNumber += 2;
|
|
continue;
|
|
|
|
case ANI_ADJUSTXY:
|
|
_ctx->frameNumber += 3;
|
|
continue;
|
|
|
|
default:
|
|
// Stop this sample
|
|
if (_ctx->sampleNumber)
|
|
_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
|
|
|
|
_ctx->sampleNumber = FROM_32(pAni[_ctx->frameNumber++].op);
|
|
if (_ctx->sampleNumber > 0)
|
|
_ctx->bLooped = false;
|
|
else {
|
|
_ctx->sampleNumber = ~_ctx->sampleNumber;
|
|
_ctx->bLooped = true;
|
|
}
|
|
x = (short)(FROM_32(pAni[_ctx->frameNumber].op) >> 16);
|
|
y = (short)(FROM_32(pAni[_ctx->frameNumber].op) & 0xffff);
|
|
|
|
if (x == 0)
|
|
x = -1;
|
|
|
|
_vm->_sound->playSample(_ctx->sampleNumber, 0, _ctx->bLooped, x, y, PRIORITY_SCRIPT,
|
|
Audio::Mixer::kSFXSoundType);
|
|
|
|
break;
|
|
}
|
|
|
|
CORO_SLEEP(_ctx->speed);
|
|
_ctx->frameNumber++;
|
|
|
|
if (_ctx->reelActor && GetActorPresFilm(_ctx->reelActor) != hFilm) {
|
|
// Stop this sample if repeating
|
|
if (_ctx->sampleNumber && _ctx->bLooped)
|
|
_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
|
|
|
|
_ctx->bFinished = true;
|
|
}
|
|
|
|
if (myescEvent && myescEvent != GetEscEvents()) {
|
|
// Stop this sample
|
|
if (_ctx->sampleNumber)
|
|
_vm->_sound->stopSpecSample(_ctx->sampleNumber, 0);
|
|
|
|
_ctx->bFinished = true;
|
|
}
|
|
} while (!_ctx->bFinished && _ctx->myNum == g_soundReelNumbers[_ctx->myId]);
|
|
|
|
// De-register - if not been replaced
|
|
if (_ctx->myNum == g_soundReelNumbers[_ctx->myId])
|
|
DeRegisterSoundReel(hFilm, column);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
static void ResSoundReel(CORO_PARAM, const void *param) {
|
|
// get the stuff copied to process when it was created
|
|
int i = *(const int *)param;
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, g_soundReels[i].hFilm, g_soundReels[i].column,
|
|
-1, 0, g_soundReels[i].actorCol));
|
|
|
|
CORO_KILL_SELF();
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
static void SoundReelWaitCheck() {
|
|
if (--g_soundReelWait == 0) {
|
|
for (int i = 0; i < MAX_SOUNDREELS; i++) {
|
|
if (g_soundReels[i].hFilm) {
|
|
CoroScheduler.createProcess(PID_REEL, ResSoundReel, &i, sizeof(i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* - Don't bother if this reel is already playing for this actor.
|
|
* - If explicit co-ordinates, use these, If embedded co-ordinates,
|
|
* leave alone, otherwise use actor's current position.
|
|
* - Moving actors get hidden during this play, other actors get
|
|
* _ctx->replaced by this play.
|
|
* - Column 0 of a film gets its appropriate Z-position, slave columns
|
|
* get slightly bigger Z-positions, in column order.
|
|
* - Play proceeds until the script finishes, another reel starts up for
|
|
* this actor, or the actor gets killed.
|
|
* - If called from an splay(), moving actor's co-ordinates are updated
|
|
* after the play, any walk still in progress will go on from there.
|
|
*/
|
|
static void t1PlayReel(CORO_PARAM, const PPINIT *ppi) {
|
|
CORO_BEGIN_CONTEXT;
|
|
OBJECT *pPlayObj; // Object
|
|
ANIM thisAnim; // Animation structure
|
|
|
|
bool mActor; // Gets set if this is a moving actor
|
|
bool lifeNoMatter;
|
|
bool replaced;
|
|
|
|
const FREEL *pfreel; // The 'column' to play
|
|
int stepCount;
|
|
int frameCount;
|
|
int reelActor;
|
|
PMOVER pActor;
|
|
int tmpX, tmpY;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
// FIXME: Avoid non-const global vars
|
|
static int firstColZ = 0; // Z-position of column zero
|
|
static int32 fColZfactor = 0; // Z-factor of column zero's actor
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
const MULTI_INIT *pmi; // MULTI_INIT structure
|
|
bool bNewMover; // Gets set if a moving actor that isn't in scene yet
|
|
|
|
const FILM *pfilm;
|
|
|
|
_ctx->lifeNoMatter = false;
|
|
_ctx->replaced = false;
|
|
_ctx->pActor = NULL;
|
|
bNewMover = false;
|
|
|
|
pfilm = (const FILM *)LockMem(ppi->hFilm);
|
|
_ctx->pfreel = &pfilm->reels[ppi->column];
|
|
|
|
// Get the MULTI_INIT structure
|
|
pmi = (const MULTI_INIT *)LockMem(FROM_32(_ctx->pfreel->mobj));
|
|
|
|
// Save actor's ID
|
|
_ctx->reelActor = (int32)FROM_32(pmi->mulID);
|
|
|
|
/**** New (experimental? bit 5/1/95 ****/
|
|
if (!TinselV0 && !actorAlive(_ctx->reelActor))
|
|
return;
|
|
/**** Delete a bit down there if this stays ****/
|
|
|
|
UpdateActorEsc(_ctx->reelActor, ppi->escOn, ppi->myescEvent);
|
|
|
|
// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
|
|
if (ppi->hFilm != GetActorLatestFilm(_ctx->reelActor)) {
|
|
// This in not the last film scheduled for this actor
|
|
|
|
// It may be the last non-talk film though
|
|
if (ActorIsTalking(_ctx->reelActor))
|
|
SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
|
|
|
|
return;
|
|
}
|
|
if (ActorIsTalking(_ctx->reelActor)) {
|
|
// Note: will delete this and there'll be no need to store the talk film!
|
|
if (ppi->hFilm != GetActorTalkFilm(_ctx->reelActor)) {
|
|
SetActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk
|
|
return;
|
|
}
|
|
} else {
|
|
SetActorPlayFilm(_ctx->reelActor, ppi->hFilm);
|
|
}
|
|
|
|
// If this reel is already playing for this actor, just forget it.
|
|
if (actorReel(_ctx->reelActor) == _ctx->pfreel)
|
|
return;
|
|
|
|
// Poke in the background palette
|
|
PokeInPalette(FROM_32(pmi->hMulFrame));
|
|
|
|
// Set up and insert the multi-object
|
|
_ctx->pPlayObj = MultiInitObject(pmi);
|
|
if (!ppi->bTop)
|
|
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
|
|
else
|
|
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
|
|
|
|
// If co-ordinates are specified, use specified.
|
|
// Otherwise, use actor's position if there are not embedded co-ords.
|
|
// Add this first test for nth columns with offsets
|
|
// in plays with (x,y)
|
|
_ctx->tmpX = ppi->x;
|
|
_ctx->tmpY = ppi->y;
|
|
if (ppi->column != 0 && (pmi->mulX || pmi->mulY)) {
|
|
} else if (_ctx->tmpX != -1 || _ctx->tmpY != -1) {
|
|
MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
|
|
} else if (!pmi->mulX && !pmi->mulY) {
|
|
GetActorPos(_ctx->reelActor, &_ctx->tmpX, &_ctx->tmpY);
|
|
MultiSetAniXY(_ctx->pPlayObj, _ctx->tmpX, _ctx->tmpY);
|
|
}
|
|
|
|
// If it's a moving actor, this hides the moving actor
|
|
// used to do this only if (actorid == 0) - I don't know why
|
|
_ctx->mActor = HideMovingActor(_ctx->reelActor, ppi->sf);
|
|
|
|
// If it's a moving actor, get its MOVER structure.
|
|
// If it isn't in the scene yet, get its task running - using
|
|
// Stand() - to prevent a glitch at the end of the play.
|
|
if (_ctx->mActor) {
|
|
_ctx->pActor = GetMover(_ctx->reelActor);
|
|
if (!getMActorState(_ctx->pActor)) {
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, _ctx->reelActor, MAGICX, MAGICY, 0));
|
|
bNewMover = true;
|
|
}
|
|
}
|
|
|
|
// Register the fact that we're playing this for this actor
|
|
storeActorReel(_ctx->reelActor, _ctx->pfreel, ppi->hFilm, _ctx->pPlayObj, ppi->column, _ctx->tmpX, _ctx->tmpY);
|
|
|
|
/**** Will get rid of this if the above is kept ****/
|
|
// We may be temporarily resuscitating a dead actor
|
|
if (ppi->actorid == 0 && !actorAlive(_ctx->reelActor))
|
|
_ctx->lifeNoMatter = true;
|
|
|
|
InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_32(_ctx->pfreel->script), ppi->speed);
|
|
|
|
// If first column, set Z position as per
|
|
// Otherwise, column 0's + column number
|
|
// N.B. It HAS been ensured that the first column gets here first
|
|
if (ppi->z != 0) {
|
|
MultiSetZPosition(_ctx->pPlayObj, ppi->z);
|
|
StoreActorZpos(_ctx->reelActor, ppi->z);
|
|
} else if (ppi->bTop) {
|
|
if (ppi->column == 0) {
|
|
firstColZ = Z_TOPPLAY + actorMaskType(_ctx->reelActor);
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
|
|
StoreActorZpos(_ctx->reelActor, firstColZ);
|
|
} else {
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
|
|
StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
|
|
}
|
|
} else if (ppi->column == 0) {
|
|
if (_ctx->mActor && !bNewMover) {
|
|
// If no path, just use first path in the scene
|
|
if (_ctx->pActor->hCpath == NOPOLY)
|
|
fColZfactor = GetPolyZfactor(FirstPathPoly());
|
|
else
|
|
fColZfactor = GetPolyZfactor(_ctx->pActor->hCpath);
|
|
firstColZ = AsetZPos(_ctx->pPlayObj, MultiLowest(_ctx->pPlayObj), fColZfactor);
|
|
} else {
|
|
switch (actorMaskType(_ctx->reelActor)) {
|
|
case ACT_DEFAULT:
|
|
fColZfactor = 0;
|
|
firstColZ = 2;
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
|
|
break;
|
|
case ACT_MASK:
|
|
fColZfactor = 0;
|
|
firstColZ = MultiLowest(_ctx->pPlayObj);
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
|
|
break;
|
|
case ACT_ALWAYS:
|
|
fColZfactor = 10;
|
|
firstColZ = 10000;
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
|
|
break;
|
|
default:
|
|
fColZfactor = actorMaskType(_ctx->reelActor);
|
|
firstColZ = AsetZPos(_ctx->pPlayObj, MultiLowest(_ctx->pPlayObj), fColZfactor);
|
|
if (firstColZ < 2) {
|
|
// This is an experiment!
|
|
firstColZ = 2;
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
StoreActorZpos(_ctx->reelActor, firstColZ);
|
|
} else {
|
|
if (NoNameFunc(_ctx->reelActor, bNewMover) > fColZfactor) {
|
|
fColZfactor = NoNameFunc(_ctx->reelActor, bNewMover);
|
|
firstColZ = fColZfactor << 10;
|
|
}
|
|
MultiSetZPosition(_ctx->pPlayObj, firstColZ + ppi->column);
|
|
StoreActorZpos(_ctx->reelActor, firstColZ + ppi->column);
|
|
}
|
|
|
|
/*
|
|
* Play until the script finishes,
|
|
* another reel starts up for this actor,
|
|
* or the actor gets killed.
|
|
*/
|
|
_ctx->stepCount = 0;
|
|
_ctx->frameCount = 0;
|
|
do {
|
|
if (_ctx->stepCount++ == 0) {
|
|
_ctx->frameCount++;
|
|
StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
|
|
}
|
|
if (_ctx->stepCount == ppi->speed)
|
|
_ctx->stepCount = 0;
|
|
|
|
if (StepAnimScript(&_ctx->thisAnim) == ScriptFinished)
|
|
break;
|
|
|
|
int x, y;
|
|
GetAniPosition(_ctx->pPlayObj, &x, &y);
|
|
StoreActorPos(_ctx->reelActor, x, y);
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
if (actorReel(_ctx->reelActor) != _ctx->pfreel) {
|
|
_ctx->replaced = true;
|
|
break;
|
|
}
|
|
|
|
if (ActorEsc(_ctx->reelActor) && ActorEev(_ctx->reelActor) != GetEscEvents())
|
|
break;
|
|
|
|
} while (_ctx->lifeNoMatter || actorAlive(_ctx->reelActor));
|
|
|
|
// Register the fact that we're NOT playing this for this actor
|
|
if (actorReel(_ctx->reelActor) == _ctx->pfreel)
|
|
storeActorReel(_ctx->reelActor, NULL, 0, NULL, 0, 0, 0);
|
|
|
|
// Ditch the object
|
|
if (!ppi->bTop)
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
|
|
else
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
|
|
|
|
if (_ctx->mActor) {
|
|
if (!_ctx->replaced)
|
|
unHideMovingActor(_ctx->reelActor); // Restore moving actor
|
|
|
|
// Update it's co-ordinates if this is an splay()
|
|
if (ppi->splay)
|
|
restoreMovement(_ctx->reelActor);
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* - Don't bother if this reel is already playing for this actor.
|
|
* - If explicit co-ordinates, use these, If embedded co-ordinates,
|
|
* leave alone, otherwise use actor's current position.
|
|
* - Moving actors get hidden during this play, other actors get
|
|
* replaced by this play.
|
|
* - Column 0 of a film gets its appropriate Z-position, slave columns
|
|
* get slightly bigger Z-positions, in column order.
|
|
* - Play proceeds until the script finishes, another reel starts up for
|
|
* this actor, or the actor gets killed.
|
|
* - If called from an splay(), moving actor's co-ordinates are updated
|
|
* after the play, any walk still in progress will go on from there.
|
|
* @param x Co-ordinates from the play(), set to (-1, -1) if none
|
|
* @param y Co-ordinates from the play(), set to (-1, -1) if none
|
|
* @param bRestore Normally False, set if from restore
|
|
* @param speed Film speed
|
|
* @param hFilm The 'film'
|
|
* @param column Column number, first column = 0
|
|
*/
|
|
static void t2PlayReel(CORO_PARAM, int x, int y, bool bRestore, int speed, SCNHANDLE hFilm,
|
|
int column, int myescEvent, bool bTop) {
|
|
CORO_BEGIN_CONTEXT;
|
|
bool bReplaced;
|
|
bool bGotHidden;
|
|
int stepCount;
|
|
int frameCount;
|
|
bool bEscapedAlready;
|
|
bool bPrinciple; // true if this is the first column in a film for one actor
|
|
bool bRelative; // true if relative specified in script
|
|
|
|
FREEL *pFreel;
|
|
MULTI_INIT *pmi; // MULTI_INIT structure
|
|
POBJECT pPlayObj; // Object
|
|
ANIM thisAnim; // Animation structure
|
|
|
|
int reelActor; // Which actor this reel belongs to
|
|
PMOVER pMover; // set if it's a moving actor
|
|
bool bNewMover; // Gets set if a moving actor that isn't in scene yet
|
|
|
|
int filmNumber;
|
|
int myZ; // Remember for hide/unhide
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->bReplaced = false;
|
|
_ctx->bGotHidden = false;
|
|
_ctx->stepCount = 0;
|
|
_ctx->frameCount = 0;
|
|
|
|
_ctx->bEscapedAlready = false;
|
|
|
|
// Get the reel and MULTI_INIT structure
|
|
_ctx->pFreel = GetReel(hFilm, column);
|
|
_ctx->pmi = (MULTI_INIT *)LockMem(FROM_32(_ctx->pFreel->mobj));
|
|
|
|
if ((int32)FROM_32(_ctx->pmi->mulID) == -2) {
|
|
CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, hFilm, column, speed, myescEvent,
|
|
FROM_32(_ctx->pmi->otherFlags) & OTH_RELATEDACTOR));
|
|
return;
|
|
}
|
|
|
|
// Save actor's ID
|
|
_ctx->reelActor = FROM_32(_ctx->pmi->mulID);
|
|
|
|
UpdateActorEsc(_ctx->reelActor, myescEvent);
|
|
|
|
// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
|
|
if (hFilm != GetActorLatestFilm(_ctx->reelActor)) {
|
|
// This in not the last film scheduled for this actor
|
|
|
|
// It may be the last non-talk film though
|
|
if (ActorIsTalking(_ctx->reelActor))
|
|
SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
|
|
|
|
return;
|
|
}
|
|
if (ActorIsTalking(_ctx->reelActor)) {
|
|
// Note: will delete this and there'll be no need to store the talk film!
|
|
if (hFilm != GetActorTalkFilm(_ctx->reelActor)) {
|
|
SetActorPlayFilm(_ctx->reelActor, hFilm); // Revert to this film after talk
|
|
return;
|
|
}
|
|
} else {
|
|
SetActorPlayFilm(_ctx->reelActor, hFilm);
|
|
}
|
|
|
|
// Register the film for this actor
|
|
if (hFilm != GetActorPresFilm(_ctx->reelActor)) {
|
|
_ctx->bPrinciple = true;
|
|
StoreActorPresFilm(_ctx->reelActor, hFilm, x, y);
|
|
} else {
|
|
_ctx->bPrinciple = false;
|
|
|
|
// If this reel is already playing for this actor, just forget it.
|
|
if (ActorReelPlaying(_ctx->reelActor, column))
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Insert the object
|
|
*/
|
|
// Poke in the background palette
|
|
PokeInPalette(_ctx->pmi);
|
|
|
|
// Set ghost bit if wanted
|
|
if (ActorIsGhost(_ctx->reelActor)) {
|
|
assert(FROM_32(_ctx->pmi->mulFlags) == DMA_WNZ || FROM_32(_ctx->pmi->mulFlags) == (DMA_WNZ | DMA_GHOST));
|
|
_ctx->pmi->mulFlags = TO_32(FROM_32(_ctx->pmi->mulFlags) | DMA_GHOST);
|
|
}
|
|
|
|
// Set up and insert the multi-object
|
|
_ctx->pPlayObj = MultiInitObject(_ctx->pmi);
|
|
if (!bTop)
|
|
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
|
|
else
|
|
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
|
|
|
|
/*
|
|
* More action for moving actors
|
|
*/
|
|
_ctx->pMover = GetMover(_ctx->reelActor);
|
|
if (_ctx->pMover != NULL) {
|
|
HideMover(_ctx->pMover);
|
|
|
|
if (!MoverIs(_ctx->pMover)) {
|
|
// Used to do a Stand here to prevent glitches
|
|
|
|
_ctx->bNewMover = true;
|
|
} else
|
|
_ctx->bNewMover = false;
|
|
}
|
|
|
|
// Register the reel for this actor
|
|
StoreActorReel(_ctx->reelActor, column, _ctx->pPlayObj);
|
|
|
|
_ctx->filmNumber = GetActorFilmNumber(_ctx->reelActor);
|
|
|
|
/*
|
|
* Sort out x and y
|
|
*/
|
|
assert( ((FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE))
|
|
|| ((FROM_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE) && !(FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE)) );
|
|
|
|
_ctx->bRelative = FROM_32(_ctx->pmi->otherFlags) & OTH_RELATIVE;
|
|
|
|
if (_ctx->bRelative) {
|
|
// Use actor's position. If (x, y) specified, move the actor.
|
|
if (x == -1 && y == -1)
|
|
GetActorPos(_ctx->reelActor, &x, &y);
|
|
else
|
|
StoreActorPos(_ctx->reelActor, x, y);
|
|
} else if (x == -1 && y == -1)
|
|
x = y = 0; // Use (0,0) if no specified
|
|
|
|
// Add embedded co-ords
|
|
MultiSetAniXY(_ctx->pPlayObj, x + FROM_32(_ctx->pmi->mulX), y + FROM_32(_ctx->pmi->mulY));
|
|
|
|
/*
|
|
* Sort out z
|
|
*/
|
|
if (bRestore) {
|
|
_ctx->myZ = GetActorZpos(_ctx->reelActor, column);
|
|
|
|
SoundReelWaitCheck();
|
|
} else {
|
|
// FIXME: Avoid non-const global vars
|
|
static int baseZposn; // Z-position of column zero
|
|
static uint32 baseZfact; // Z-factor of column zero's actor
|
|
|
|
// N.B. It HAS been ensured that the first column gets here first
|
|
|
|
if ((int32)FROM_32(_ctx->pmi->mulZ) != -1) {
|
|
// Z override in script
|
|
|
|
baseZfact = FROM_32(_ctx->pmi->mulZ);
|
|
baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
|
|
if (bTop)
|
|
baseZposn += Z_TOPPLAY;
|
|
} else if (column == 0
|
|
|| GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover) > baseZfact) {
|
|
// Subsequent columns are based on this one
|
|
|
|
baseZfact = GetZfactor(_ctx->reelActor, _ctx->pMover, _ctx->bNewMover);
|
|
baseZposn = (baseZfact << ZSHIFT) + MultiLowest(_ctx->pPlayObj);
|
|
if (bTop)
|
|
baseZposn += Z_TOPPLAY;
|
|
}
|
|
_ctx->myZ = baseZposn + column;
|
|
}
|
|
MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
|
|
StoreActorZpos(_ctx->reelActor, _ctx->myZ, column);
|
|
|
|
/*
|
|
* Play until the script finishes,
|
|
* another reel starts up for this actor,
|
|
* or the actor gets killed.
|
|
*/
|
|
InitStepAnimScript(&_ctx->thisAnim, _ctx->pPlayObj, FROM_32(_ctx->pFreel->script), speed);
|
|
|
|
if (bRestore || (ActorEsc(_ctx->reelActor) == true &&
|
|
ActorEev(_ctx->reelActor) != GetEscEvents())) {
|
|
// From restore, step to jump or end
|
|
SkipFrames(&_ctx->thisAnim, -1);
|
|
}
|
|
|
|
for (;;) {
|
|
if (_ctx->stepCount++ == 0) {
|
|
_ctx->frameCount++;
|
|
StoreActorSteps(_ctx->reelActor, _ctx->frameCount);
|
|
}
|
|
if (_ctx->stepCount == speed)
|
|
_ctx->stepCount = 0;
|
|
|
|
if (_ctx->bPrinciple && AboutToJumpOrEnd(&_ctx->thisAnim))
|
|
IncLoopCount(_ctx->reelActor);
|
|
|
|
if (StepAnimScript(&_ctx->thisAnim) == ScriptFinished)
|
|
break;
|
|
|
|
if (_ctx->bRelative) {
|
|
GetAniPosition(_ctx->pPlayObj, &x, &y);
|
|
StoreActorPos(_ctx->reelActor, x, y);
|
|
}
|
|
|
|
if (_ctx->bGotHidden) {
|
|
if (!ActorHidden(_ctx->reelActor)) {
|
|
MultiSetZPosition(_ctx->pPlayObj, _ctx->myZ);
|
|
_ctx->bGotHidden = false;
|
|
}
|
|
} else {
|
|
if (ActorHidden(_ctx->reelActor)) {
|
|
MultiSetZPosition(_ctx->pPlayObj, -1);
|
|
_ctx->bGotHidden = true;
|
|
}
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
if (GetActorFilmNumber(_ctx->reelActor) != _ctx->filmNumber) {
|
|
_ctx->bReplaced = true;
|
|
break;
|
|
}
|
|
|
|
if (ActorEsc(_ctx->reelActor) == true && ActorEev(_ctx->reelActor) != GetEscEvents()) {
|
|
if (!_ctx->bEscapedAlready) {
|
|
SkipFrames(&_ctx->thisAnim, -1);
|
|
_ctx->bEscapedAlready = true;
|
|
}
|
|
|
|
//WHY??? UpdateActorEsc(reelActor, GetEscEvents());
|
|
// The above line of code, not commented out would fix the coffee pot flash
|
|
// but why was it commented out?
|
|
// The extra boolean is used instead, 'cos it's release week and I want to play it safe!
|
|
}
|
|
}
|
|
|
|
// Register the fact that we're NOT playing this for this actor
|
|
NotPlayingReel(_ctx->reelActor, _ctx->filmNumber, column);
|
|
|
|
// Ditch the object
|
|
if (!bTop)
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), _ctx->pPlayObj);
|
|
else
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pPlayObj);
|
|
|
|
// Restore moving actor is nessesary
|
|
if (_ctx->pMover != NULL && _ctx->bPrinciple && !_ctx->bReplaced)
|
|
UnHideMover(_ctx->pMover);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Run all animations that comprise the play film.
|
|
*/
|
|
static void PlayProcess(CORO_PARAM, const void *param) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
const PPINIT *ppi = (const PPINIT *)param;
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (TinselV2)
|
|
CORO_INVOKE_ARGS(t2PlayReel, (CORO_SUBCTX, ppi->x, ppi->y, ppi->bRestore, ppi->speed,
|
|
ppi->hFilm, ppi->column, ppi->myescEvent, ppi->bTop));
|
|
else
|
|
CORO_INVOKE_1(t1PlayReel, ppi);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
// *******************************************************
|
|
|
|
|
|
// To handle the play()-talk(), talk()-play(), talk()-talk() and play()-play() scenarios
|
|
void NewestFilm(SCNHANDLE film, const FREEL *reel) {
|
|
const MULTI_INIT *pmi; // MULTI_INIT structure
|
|
|
|
// Get the MULTI_INIT structure
|
|
pmi = (const MULTI_INIT *)LockMem(FROM_32(reel->mobj));
|
|
|
|
if (!TinselV2 || ((int32)FROM_32(pmi->mulID) != -2))
|
|
SetActorLatestFilm((int32)FROM_32(pmi->mulID), film);
|
|
}
|
|
|
|
// *******************************************************
|
|
|
|
/**
|
|
* Start up a play process for each column in a film.
|
|
*
|
|
* NOTE: The processes are started in reverse order so that the first
|
|
* column's process kicks in first.
|
|
*/
|
|
void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact, bool escOn,
|
|
int myescEvent, bool bTop) {
|
|
assert(hFilm != 0); // Trying to play NULL film
|
|
const FILM *pFilm;
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
pFilm = (const FILM *)LockMem(hFilm);
|
|
PPINIT ppi;
|
|
|
|
// Now allowed empty films!
|
|
if (pFilm->numreels == 0)
|
|
return; // Nothing to do!
|
|
|
|
ppi.hFilm = hFilm;
|
|
ppi.x = x;
|
|
ppi.y = y;
|
|
ppi.z = 0;
|
|
ppi.bRestore = false;
|
|
ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate));
|
|
ppi.actorid = actorid;
|
|
ppi.splay = splay;
|
|
ppi.bTop = bTop;
|
|
ppi.sf = sfact;
|
|
ppi.escOn = escOn;
|
|
ppi.myescEvent = myescEvent;
|
|
|
|
// Start display process for each reel in the film
|
|
for (int i = FROM_32(pFilm->numreels) - 1; i >= 0; i--) {
|
|
NewestFilm(hFilm, &pFilm->reels[i]);
|
|
|
|
ppi.column = i;
|
|
CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(PPINIT));
|
|
}
|
|
|
|
if (TinselV2) {
|
|
// Let it all kick in and position this process
|
|
// down the process list from the playing process(es)
|
|
// This ensures something
|
|
CORO_GIVE_WAY;
|
|
|
|
if (myescEvent && myescEvent != GetEscEvents())
|
|
CoroScheduler.rescheduleAll();
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop) {
|
|
PlayFilm(coroParam, hFilm, x, y, 0, false, false, false, myescEvent, bTop);
|
|
}
|
|
|
|
/**
|
|
* Start up a play process for each slave column in a film.
|
|
* Play the first column directly from the parent process.
|
|
*/
|
|
void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact,
|
|
bool escOn, int myescEvent, bool bTop) {
|
|
CORO_BEGIN_CONTEXT;
|
|
PPINIT ppi;
|
|
int i;
|
|
int loopCount;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
assert(hFilm != 0); // Trying to play NULL film
|
|
const FILM *pFilm;
|
|
|
|
pFilm = (const FILM *)LockMem(hFilm);
|
|
|
|
// Now allowed empty films!
|
|
if (pFilm->numreels == 0)
|
|
return; // Already played to completion!
|
|
|
|
_ctx->ppi.hFilm = hFilm;
|
|
_ctx->ppi.x = x;
|
|
_ctx->ppi.y = y;
|
|
_ctx->ppi.z = 0;
|
|
_ctx->ppi.bRestore = false;
|
|
_ctx->ppi.speed = (ONE_SECOND / FROM_32(pFilm->frate));
|
|
_ctx->ppi.actorid = actorid;
|
|
_ctx->ppi.splay = splay;
|
|
_ctx->ppi.bTop = bTop;
|
|
_ctx->ppi.sf = sfact;
|
|
_ctx->ppi.escOn = escOn;
|
|
_ctx->ppi.myescEvent = myescEvent;
|
|
|
|
// Start display process for each secondary reel in the film in Tinsel 1,
|
|
// or all of them in Tinsel 2
|
|
for (int i = FROM_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) {
|
|
NewestFilm(hFilm, &pFilm->reels[i]);
|
|
|
|
_ctx->ppi.column = i;
|
|
CoroScheduler.createProcess(PID_REEL, PlayProcess, &_ctx->ppi, sizeof(PPINIT));
|
|
}
|
|
|
|
if (TinselV2) {
|
|
// Let it all kick in and position this 'waiting' process
|
|
// down the process list from the playing process(es)
|
|
// This ensures immediate return when the reel finishes
|
|
CORO_GIVE_WAY;
|
|
|
|
_ctx->i = ExtractActor(hFilm);
|
|
_ctx->loopCount = GetLoopCount(_ctx->i);
|
|
|
|
// Wait until film changes or loop count increases
|
|
while (GetActorPresFilm(_ctx->i) == hFilm && GetLoopCount(_ctx->i) == _ctx->loopCount) {
|
|
if (myescEvent && myescEvent != GetEscEvents()) {
|
|
CoroScheduler.rescheduleAll();
|
|
break;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
} else {
|
|
// For Tinsel 1, launch the primary reel
|
|
NewestFilm(hFilm, &pFilm->reels[0]);
|
|
|
|
_ctx->ppi.column = 0;
|
|
CORO_INVOKE_1(t1PlayReel, &_ctx->ppi);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Start up a play process for a particular column in a film.
|
|
*
|
|
* NOTE: This is specifically for actors during a Tinsel 1 restore scene.
|
|
*/
|
|
void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y) {
|
|
assert(!TinselV2);
|
|
const FILM *pfilm = (const FILM *)LockMem(hFilm);
|
|
PPINIT ppi;
|
|
|
|
ppi.hFilm = hFilm;
|
|
ppi.x = x;
|
|
ppi.y = y;
|
|
ppi.z = z;
|
|
ppi.speed = (ONE_SECOND / FROM_32(pfilm->frate));
|
|
ppi.actorid = 0;
|
|
ppi.splay = false;
|
|
ppi.bTop = false;
|
|
ppi.bRestore = true;
|
|
ppi.sf = 0;
|
|
ppi.column = reelnum;
|
|
ppi.myescEvent = 0;
|
|
|
|
ppi.escOn = false;
|
|
ppi.myescEvent = GetEscEvents();
|
|
|
|
assert(pfilm->numreels);
|
|
|
|
NewestFilm(hFilm, &pfilm->reels[reelnum]);
|
|
|
|
// Start display process for the reel
|
|
CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
|
|
}
|
|
|
|
/**
|
|
* Start up a play process for a particular column in a film.
|
|
*
|
|
* NOTE: This is specifically for actors during a Tinsel 2 restore scene.
|
|
*/
|
|
void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y) {
|
|
assert(TinselV2);
|
|
FILM *pFilm = (FILM *)LockMem(hFilm);
|
|
PPINIT ppi;
|
|
|
|
int i;
|
|
FREEL *pFreel;
|
|
PMULTI_INIT pmi; // MULTI_INIT structure
|
|
|
|
ppi.hFilm = hFilm;
|
|
ppi.x = (short)x;
|
|
ppi.y = (short)y;
|
|
ppi.bRestore = true;
|
|
ppi.speed = (short)(ONE_SECOND/FROM_32(pFilm->frate));
|
|
ppi.bTop = false;
|
|
ppi.myescEvent = 0;
|
|
|
|
// Search backwards for now as later column will be the one
|
|
for (i = (int)FROM_32(pFilm->numreels) - 1; i >= 0; i--) {
|
|
pFreel = &pFilm->reels[i];
|
|
pmi = (PMULTI_INIT) LockMem(FROM_32(pFreel->mobj));
|
|
if ((int32)FROM_32(pmi->mulID) == actor) {
|
|
ppi.column = (short)i;
|
|
NewestFilm(hFilm, &pFilm->reels[i]);
|
|
|
|
// Start display process for the reel
|
|
CoroScheduler.createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
|
|
|
|
g_soundReelWait++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the actor id from a film (column 0)
|
|
*/
|
|
int ExtractActor(SCNHANDLE hFilm) {
|
|
const FILM *pFilm = (const FILM *)LockMem(hFilm);
|
|
const FREEL *pReel = &pFilm->reels[0];
|
|
const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_32(pReel->mobj));
|
|
return (int)FROM_32(pmi->mulID);
|
|
}
|
|
|
|
} // End of namespace Tinsel
|