scummvm/engines/tinsel/play.cpp
Max Horn 1dbf8d73d5 TINSEL: Mark all (?) global vars with a FIXME comment
Use of global vars is what prevents RTL from working in Tinsel (and
probably in other engines). More specifically, the fact that many
global vars are not explicitly inited when the engine is (re)launched.

svn-id: r54262
2010-11-16 09:53:55 +00:00

1184 lines
33 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.
*
* $URL$
* $Id$
*
* Plays films within a scene, takes into account the actor in each 'column'. |
*/
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/coroutine.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 soundReels[MAX_SOUNDREELS];
static int soundReelNumbers[MAX_SOUNDREELS];
static int 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_LE_UINT32(pFrame)); // handle to image
pim->hImgPal = TO_LE_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_LE_32(pmi->hMulFrame));
// get pointer to image
pim = (IMAGE *)LockMem(READ_LE_UINT32(pFrame)); // handle to image
pim->hImgPal = TO_LE_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 (soundReels[i].hFilm == hFilm && soundReels[i].column == column)
break;
if (!soundReels[i].hFilm) {
soundReels[i].hFilm = hFilm;
soundReels[i].column = column;
soundReels[i].actorCol = actorCol;
break;
}
}
soundReelNumbers[i]++;
return i;
}
void NoSoundReels() {
memset(soundReels, 0, sizeof(soundReels));
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 (soundReels[i].hFilm == hFilm && soundReels[i].column == column) {
soundReels[i].hFilm = 0;
break;
}
}
}
void SaveSoundReels(PSOUNDREELS psr) {
for (int i = 0; i < MAX_SOUNDREELS; i++) {
if (IsCdPlayHandle(soundReels[i].hFilm))
soundReels[i].hFilm = 0;
}
memcpy(psr, soundReels, sizeof(soundReels));
}
void RestoreSoundReels(PSOUNDREELS psr) {
memcpy(soundReels, psr, sizeof(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_LE_32(pReel->mobj));
_ctx->reelActor = (int32)FROM_LE_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 = soundReelNumbers[_ctx->myId];
do {
pFilm = (FILM *)LockMem(hFilm);
pReel = &pFilm->reels[column];
pAni = (ANI_SCRIPT *)LockMem(FROM_LE_32(pReel->script));
if (_ctx->speed == -1) {
_ctx->speed = (ONE_SECOND/FROM_LE_32(pFilm->frate));
// Restored reel
for (;;) {
if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_END)
break;
else if (FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_JUMP) {
_ctx->frameNumber++;
_ctx->frameNumber += FROM_LE_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_LE_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTX
|| FROM_LE_32(pAni[_ctx->frameNumber].op) == ANI_ADJUSTY) {
_ctx->frameNumber += 2;
} else if (FROM_LE_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_LE_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_LE_32(pAni[_ctx->frameNumber].op) < 0);
_ctx->frameNumber += FROM_LE_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_LE_32(pAni[_ctx->frameNumber++].op);
if (_ctx->sampleNumber > 0)
_ctx->bLooped = false;
else {
_ctx->sampleNumber = ~_ctx->sampleNumber;
_ctx->bLooped = true;
}
x = (short)(FROM_LE_32(pAni[_ctx->frameNumber].op) >> 16);
y = (short)(FROM_LE_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 == soundReelNumbers[_ctx->myId]);
// De-register - if not been replaced
if (_ctx->myNum == 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, soundReels[i].hFilm, soundReels[i].column,
-1, 0, soundReels[i].actorCol));
CORO_KILL_SELF();
CORO_END_CODE;
}
static void SoundReelWaitCheck() {
if (--soundReelWait == 0) {
for (int i = 0; i < MAX_SOUNDREELS; i++) {
if (soundReels[i].hFilm) {
g_scheduler->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_LE_32(_ctx->pfreel->mobj));
// Save actor's ID
_ctx->reelActor = (int32)FROM_LE_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_LE_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_LE_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_LE_32(_ctx->pFreel->mobj));
if ((int32)FROM_LE_32(_ctx->pmi->mulID) == -2) {
CORO_INVOKE_ARGS(SoundReel, (CORO_SUBCTX, hFilm, column, speed, myescEvent,
FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATEDACTOR));
return;
}
// Save actor's ID
_ctx->reelActor = FROM_LE_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_LE_32(_ctx->pmi->mulFlags) == DMA_WNZ || FROM_LE_32(_ctx->pmi->mulFlags) == (DMA_WNZ | DMA_GHOST));
_ctx->pmi->mulFlags = TO_LE_32(FROM_LE_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_LE_32(_ctx->pmi->otherFlags) & OTH_RELATIVE) && !(FROM_LE_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE))
|| ((FROM_LE_32(_ctx->pmi->otherFlags) & OTH_ABSOLUTE) && !(FROM_LE_32(_ctx->pmi->otherFlags) & OTH_RELATIVE)) );
_ctx->bRelative = FROM_LE_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_LE_32(_ctx->pmi->mulX), y + FROM_LE_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_LE_32(_ctx->pmi->mulZ) != -1) {
// Z override in script
baseZfact = FROM_LE_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_LE_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_LE_32(reel->mobj));
if (!TinselV2 || ((int32)FROM_LE_32(pmi->mulID) != -2))
SetActorLatestFilm((int32)FROM_LE_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_LE_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_LE_32(pFilm->numreels) - 1; i >= 0; i--) {
NewestFilm(hFilm, &pFilm->reels[i]);
ppi.column = i;
g_scheduler->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())
g_scheduler->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_LE_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_LE_32(pFilm->numreels) - 1; i >= (TinselV2 ? 0 : 1); i--) {
NewestFilm(hFilm, &pFilm->reels[i]);
_ctx->ppi.column = i;
g_scheduler->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()) {
g_scheduler->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_LE_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
g_scheduler->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_LE_32(pFilm->frate));
ppi.bTop = false;
ppi.myescEvent = 0;
// Search backwards for now as later column will be the one
for (i = (int)FROM_LE_32(pFilm->numreels) - 1; i >= 0; i--) {
pFreel = &pFilm->reels[i];
pmi = (PMULTI_INIT) LockMem(FROM_LE_32(pFreel->mobj));
if ((int32)FROM_LE_32(pmi->mulID) == actor) {
ppi.column = (short)i;
NewestFilm(hFilm, &pFilm->reels[i]);
// Start display process for the reel
g_scheduler->createProcess(PID_REEL, PlayProcess, &ppi, sizeof(ppi));
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_LE_32(pReel->mobj));
return (int)FROM_LE_32(pmi->mulID);
}
} // End of namespace Tinsel