diff options
Diffstat (limited to 'engines/tinsel/play.cpp')
-rw-r--r-- | engines/tinsel/play.cpp | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/engines/tinsel/play.cpp b/engines/tinsel/play.cpp new file mode 100644 index 0000000000..e32fc88d3d --- /dev/null +++ b/engines/tinsel/play.cpp @@ -0,0 +1,507 @@ +/* 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/dw.h" +#include "tinsel/film.h" +#include "tinsel/handle.h" +#include "tinsel/multiobj.h" +#include "tinsel/object.h" +#include "tinsel/pid.h" +#include "tinsel/polygons.h" +#include "tinsel/rince.h" +#include "tinsel/sched.h" +#include "tinsel/timers.h" +#include "tinsel/tinlib.h" // stand() + +namespace Tinsel { + +/** + * 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(BackPal()); + } +} + + +int32 NoNameFunc(int actorID, bool bNewMover) { + PMACTOR 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; +} + +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() + int16 sf; // SlowFactor - only used for moving actors + int16 column; // Column number, first column = 0 + + uint8 escOn; + int32 myescEvent; +}; + + +/** + * - 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. + */ +void PlayReel(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; + CORO_END_CONTEXT(_ctx); + + 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 + PMACTOR pActor; + bool bNewMover; // Gets set if a moving actor that isn't in scene yet + + const FILM *pfilm; + + _ctx->lifeNoMatter = false; + _ctx->replaced = false; + 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 (!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 (isActorTalking(_ctx->reelActor)) + setActorPlayFilm(_ctx->reelActor, ppi->hFilm); // Revert to this film after talk + + return; + } + if (isActorTalking(_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) + int tmpX, tmpY; + tmpX = ppi->x; + tmpY = ppi->y; + if (ppi->column != 0 && (pmi->mulX || pmi->mulY)) { + } else if (tmpX != -1 || tmpY != -1) { + MultiSetAniXY(_ctx->pPlayObj, tmpX, tmpY); + } else if (!pmi->mulX && !pmi->mulY) { + GetActorPos(_ctx->reelActor, &tmpX, &tmpY); + MultiSetAniXY(_ctx->pPlayObj, tmpX, 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 MACTOR 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) { + pActor = GetMover(_ctx->reelActor); + if (getMActorState(pActor) == NO_MACTOR) { + stand(_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, tmpX, 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 (pActor->hCpath == NOPOLY) + fColZfactor = getPolyZfactor(FirstPathPoly()); + else + fColZfactor = getPolyZfactor(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; +} + +/** + * Run all animations that comprise the play film. + */ +static void playProcess(CORO_PARAM, const void *param) { + // get the stuff copied to process when it was created + PPINIT *ppi = (PPINIT *)param; + + PlayReel(coroParam, ppi); +} + +// ******************************************************* + + +// 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)); + + 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(SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn, + int myescEvent, bool bTop) { + const FILM *pfilm = (const FILM *)LockMem(film); + PPINIT ppi; + + assert(film != 0); // Trying to play NULL film + + // Now allowed empty films! + if (pfilm->numreels == 0) + return; // Nothing to do! + + ppi.hFilm = film; + ppi.x = x; + ppi.y = y; + ppi.z = 0; + 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(film, &pfilm->reels[i]); + + ppi.column = i; + g_scheduler->createProcess(PID_REEL, playProcess, &ppi, sizeof(PPINIT)); + } +} + +/** + * 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 film, int x, int y, int actorid, bool splay, int sfact, + bool escOn, int myescEvent, bool bTop) { + CORO_BEGIN_CONTEXT; + PPINIT ppi; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + assert(film != 0); // Trying to play NULL film + const FILM *pfilm; + + pfilm = (const FILM *)LockMem(film); + + // Now allowed empty films! + if (pfilm->numreels == 0) + return; // Already played to completion! + + _ctx->ppi.hFilm = film; + _ctx->ppi.x = x; + _ctx->ppi.y = y; + _ctx->ppi.z = 0; + _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 + for (int i = FROM_LE_32(pfilm->numreels) - 1; i > 0; i--) { + newestFilm(film, &pfilm->reels[i]); + + _ctx->ppi.column = i; + g_scheduler->createProcess(PID_REEL, playProcess, &_ctx->ppi, sizeof(PPINIT)); + } + + newestFilm(film, &pfilm->reels[0]); + + _ctx->ppi.column = 0; + CORO_INVOKE_1(PlayReel, &_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 restore scene. + */ +void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y) { + const FILM *pfilm = (const FILM *)LockMem(film); + PPINIT ppi; + + ppi.hFilm = film; + 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.sf = 0; + ppi.column = reelnum; + + // FIXME: The PlayReel play loop was previously breaking out, and then deleting objects, when + // returning to a scene because escOn and myescEvent were undefined. Need to make sure whether + // restored objects should have any particular combination of these two values + ppi.escOn = false; + ppi.myescEvent = GetEscEvents(); + + assert(pfilm->numreels); + + newestFilm(film, &pfilm->reels[reelnum]); + + // Start display process for the reel + g_scheduler->createProcess(PID_REEL, playProcess, &ppi, sizeof(ppi)); +} + +} // end of namespace Tinsel |