mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-25 11:15:00 +00:00
71d40e23f5
svn-id: r33285
508 lines
15 KiB
C++
508 lines
15 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/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
|