mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-04 08:17:40 +00:00
1026 lines
26 KiB
C++
1026 lines
26 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Should really be called "moving actors.c"
|
|
*/
|
|
|
|
#include "tinsel/actors.h"
|
|
#include "tinsel/anim.h"
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/config.h"
|
|
#include "tinsel/dw.h"
|
|
#include "tinsel/film.h"
|
|
#include "tinsel/handle.h"
|
|
#include "tinsel/dialogs.h"
|
|
#include "tinsel/mareels.h"
|
|
#include "tinsel/move.h"
|
|
#include "tinsel/multiobj.h" // multi-part object defintions etc.
|
|
#include "tinsel/object.h"
|
|
#include "tinsel/pcode.h"
|
|
#include "tinsel/pid.h"
|
|
#include "tinsel/play.h"
|
|
#include "tinsel/polygons.h"
|
|
#include "tinsel/movers.h"
|
|
#include "tinsel/sched.h"
|
|
#include "tinsel/sysvar.h"
|
|
#include "tinsel/timers.h"
|
|
#include "tinsel/tinsel.h"
|
|
#include "tinsel/token.h"
|
|
|
|
#include "common/textconsole.h"
|
|
#include "common/util.h"
|
|
|
|
namespace Tinsel {
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
// These vars are reset upon engine destruction
|
|
static MOVER g_Movers[MAX_MOVERS];
|
|
|
|
//----------------- FUNCTIONS ----------------------------
|
|
|
|
/**
|
|
* Called from ActorPalette(), normally once just after the beginning of time.
|
|
*/
|
|
void StoreMoverPalette(MOVER *pMover, int startColor, int length) {
|
|
pMover->startColor = startColor;
|
|
pMover->paletteLength = length;
|
|
}
|
|
|
|
/**
|
|
* Called from the moving actor's main loop.
|
|
*/
|
|
static void CheckBrightness(MOVER *pMover) {
|
|
int brightness;
|
|
|
|
if (pMover->hCpath == NOPOLY || pMover->bHidden)
|
|
return;
|
|
|
|
brightness = GetBrightness(pMover->hCpath, pMover->objY);
|
|
|
|
if (brightness != pMover->brightness) {
|
|
// Do it all immediately on first appearance,
|
|
// otherwise do it iteratively
|
|
|
|
if (pMover->brightness == BOGUS_BRIGHTNESS)
|
|
pMover->brightness = brightness; // all the way
|
|
else if (brightness > pMover->brightness)
|
|
pMover->brightness++; // ramp up
|
|
else
|
|
pMover->brightness--; // ramp down
|
|
|
|
DimPartPalette(_vm->_bg->BgPal(),
|
|
pMover->startColor,
|
|
pMover->paletteLength,
|
|
pMover->brightness);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called from ActorBrightness() Glitter call.
|
|
* Typically called before the moving actor is created
|
|
* at the start of a scene to cover a walk-in Play().
|
|
*/
|
|
void MoverBrightness(MOVER *pMover, int brightness) {
|
|
// Note: Like with some of the Tinsel1 code, this routine original had a process yield
|
|
// if BgPal is NULL, and has been changed for ScummVM to a simple assert
|
|
|
|
// This is changed from a ProcessGiveWay in DW2 to an assert in ScummVM
|
|
assert(_vm->_bg->BgPal());
|
|
|
|
// Do it all immediately
|
|
DimPartPalette(_vm->_bg->BgPal(), pMover->startColor, pMover->paletteLength, brightness);
|
|
|
|
// The actor is probably hidden at this point,
|
|
pMover->brightness = brightness;
|
|
}
|
|
|
|
/**
|
|
* RebootMovers
|
|
*/
|
|
void RebootMovers() {
|
|
memset(g_Movers, 0, sizeof(g_Movers));
|
|
}
|
|
|
|
/**
|
|
* Given an actor number, return pointer to its moving actor structure,
|
|
* if it is a moving actor.
|
|
*/
|
|
MOVER *GetMover(int ano) {
|
|
int i;
|
|
|
|
// Slot 0 is reserved for lead actor
|
|
if (ano == _vm->_actor->GetLeadId() || ano == LEAD_ACTOR)
|
|
return &g_Movers[0];
|
|
|
|
for (i = 1; i < MAX_MOVERS; i++)
|
|
if (g_Movers[i].actorID == ano)
|
|
return &g_Movers[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Register an actor as being a moving one.
|
|
*/
|
|
MOVER *RegisterMover(int ano) {
|
|
int i;
|
|
|
|
// Slot 0 is reserved for lead actor
|
|
if (ano == _vm->_actor->GetLeadId() || ano == LEAD_ACTOR) {
|
|
g_Movers[0].actorToken = TOKEN_LEAD;
|
|
g_Movers[0].actorID = _vm->_actor->GetLeadId();
|
|
return &g_Movers[0];
|
|
}
|
|
|
|
// Check it hasn't already been declared
|
|
for (i = 1; i < MAX_MOVERS; i++) {
|
|
if (g_Movers[i].actorID == ano) {
|
|
// Actor is already a moving actor
|
|
return &g_Movers[i];
|
|
}
|
|
}
|
|
|
|
// Find an empty slot
|
|
for (i = 1; i < MAX_MOVERS; i++)
|
|
if (!g_Movers[i].actorID) {
|
|
g_Movers[i].actorToken = TOKEN_LEAD + i;
|
|
g_Movers[i].actorID = ano;
|
|
return &g_Movers[i];
|
|
}
|
|
|
|
error("Too many moving actors");
|
|
}
|
|
|
|
/**
|
|
* Given an index, returns the associated moving actor.
|
|
*
|
|
* At the time of writing, used by the effect process.
|
|
*/
|
|
MOVER *GetLiveMover(int index) {
|
|
assert(index >= 0 && index < MAX_MOVERS); // out of range
|
|
|
|
if (g_Movers[index].bActive)
|
|
return &g_Movers[index];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
bool IsMAinEffectPoly(int index) {
|
|
assert(index >= 0 && index < MAX_MOVERS); // out of range
|
|
|
|
return g_Movers[index].bInEffect;
|
|
}
|
|
|
|
void SetMoverInEffect(int index, bool tf) {
|
|
assert(index >= 0 && index < MAX_MOVERS); // out of range
|
|
|
|
g_Movers[index].bInEffect = tf;
|
|
}
|
|
|
|
/**
|
|
* Remove a moving actor from the current scene.
|
|
*/
|
|
void KillMover(MOVER *pMover) {
|
|
if (pMover->bActive) {
|
|
pMover->bActive = false;
|
|
MultiDeleteObjectIfExists(FIELD_WORLD, &pMover->actorObj);
|
|
assert(CoroScheduler.getCurrentProcess() != pMover->pProc);
|
|
CoroScheduler.killProcess(pMover->pProc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* getMActorState
|
|
*/
|
|
bool getMActorState(MOVER *pActor) {
|
|
return pActor->bActive;
|
|
}
|
|
|
|
/**
|
|
* If the actor's object exists, move it behind the background.
|
|
* MultiHideObject() is deliberately not used, as StepAnimScript() calls
|
|
* cause the object to re-appear.
|
|
*/
|
|
void HideMover(MOVER *pMover, int sf) {
|
|
assert(pMover); // Hiding null moving actor
|
|
|
|
pMover->bHidden = true;
|
|
|
|
if (TinselVersion <= 1) {
|
|
// sf is only passed in Tinsel v1
|
|
pMover->SlowFactor = sf;
|
|
} else {
|
|
// Tinsel 2 specific code
|
|
if (_vm->_actor->IsTaggedActor(pMover->actorID)) {
|
|
// It may be pointed to
|
|
_vm->_actor->SetActorPointedTo(pMover->actorID, false);
|
|
_vm->_actor->SetActorTagWanted(pMover->actorID, false, false, 0);
|
|
}
|
|
}
|
|
|
|
if (pMover->actorObj)
|
|
MultiSetZPosition(pMover->actorObj, -1);
|
|
}
|
|
|
|
/**
|
|
* MoverHidden
|
|
*/
|
|
bool MoverHidden(MOVER *pMover) {
|
|
if (pMover)
|
|
return pMover->bHidden;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* To be or not to be? If it be, then it is.
|
|
*/
|
|
bool MoverIs(MOVER *pMover) {
|
|
if (TinselVersion >= 2)
|
|
return pMover->actorObj ? true : false;
|
|
else
|
|
return getMActorState(pMover);
|
|
}
|
|
|
|
/**
|
|
* To be SWalk()ing or not to be SWalk()ing?
|
|
*/
|
|
bool MoverIsSWalking(MOVER *pMover) {
|
|
return (MoverMoving(pMover) && pMover->bIgPath);
|
|
}
|
|
|
|
/**
|
|
* MoverMoving()
|
|
*/
|
|
bool MoverMoving(MOVER *pMover) {
|
|
if (TinselVersion <= 1)
|
|
return pMover->bMoving;
|
|
|
|
if (pMover->UtargetX == -1 && pMover->UtargetY == -1)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return an actor's walk ticket.
|
|
*/
|
|
int GetWalkNumber(MOVER *pMover) {
|
|
return pMover->walkNumber;
|
|
}
|
|
|
|
/**
|
|
* GetMoverId
|
|
*/
|
|
int GetMoverId(MOVER *pMover) {
|
|
return pMover->actorID;
|
|
}
|
|
|
|
/**
|
|
* Sets the mover Z position
|
|
*/
|
|
void SetMoverZ(MOVER *pMover, int y, uint32 zFactor) {
|
|
if (!pMover->bHidden) {
|
|
if (TinselVersion <= 1)
|
|
_vm->_actor->AsetZPos(pMover->actorObj, y, zFactor);
|
|
else if (MoverIsSWalking(pMover) && pMover->zOverride != -1) {
|
|
// Special for SWalk()
|
|
MultiSetZPosition(pMover->actorObj, (pMover->zOverride << ZSHIFT) + y);
|
|
} else {
|
|
// Normal case
|
|
MultiSetZPosition(pMover->actorObj, (zFactor << ZSHIFT) + y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetMoverZoverride(MOVER *pMover, uint32 zFactor) {
|
|
pMover->zOverride = zFactor;
|
|
}
|
|
|
|
/**
|
|
* UnHideMover
|
|
*/
|
|
void UnHideMover(MOVER *pMover) {
|
|
assert(pMover); // unHiding null moving actor
|
|
|
|
if ((TinselVersion <= 1) || pMover->bHidden) {
|
|
pMover->bHidden = false;
|
|
|
|
// Make visible on the screen
|
|
if (pMover->actorObj) {
|
|
// If no path, just use first path in the scene
|
|
if (pMover->hCpath != NOPOLY)
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
|
|
else
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear everything out at actor start-up time.
|
|
*/
|
|
static void InitMover(MOVER *pMover) {
|
|
pMover->bActive = false;
|
|
pMover->actorObj = nullptr;
|
|
pMover->objX = pMover->objY = 0;
|
|
|
|
pMover->hRpath = NOPOLY;
|
|
|
|
pMover->targetX = pMover->targetY = -1;
|
|
pMover->ItargetX = pMover->ItargetY = -1;
|
|
pMover->hIpath = NOPOLY;
|
|
pMover->UtargetX = pMover->UtargetY = -1;
|
|
pMover->hUpath = NOPOLY;
|
|
pMover->hCpath = NOPOLY;
|
|
|
|
pMover->over = false;
|
|
pMover->InDifficulty = NO_PROB;
|
|
|
|
pMover->hFnpath = NOPOLY;
|
|
pMover->npstatus = NOT_IN;
|
|
pMover->line = 0;
|
|
|
|
pMover->Tline = 0;
|
|
|
|
if (pMover->direction != FORWARD && pMover->direction != AWAY
|
|
&& pMover->direction != LEFTREEL && pMover->direction != RIGHTREEL)
|
|
pMover->direction = FORWARD;
|
|
|
|
if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
|
|
pMover->scale = 1;
|
|
|
|
pMover->brightness = BOGUS_BRIGHTNESS; // Force initial setup
|
|
|
|
pMover->bNoPath = false;
|
|
pMover->bIgPath = false;
|
|
pMover->bHidden = false; // 20/2/95
|
|
pMover->bStop = false;
|
|
|
|
pMover->walkNumber= 0;
|
|
pMover->stepCount = 0;
|
|
|
|
pMover->bWalkReel = false;
|
|
pMover->bSpecReel = false;
|
|
pMover->hLastFilm = 0;
|
|
pMover->hPushedFilm = 0;
|
|
|
|
pMover->bInEffect = false;
|
|
|
|
pMover->walkedFromX = pMover->walkedFromY = 0;
|
|
}
|
|
|
|
/**
|
|
* Get it into our heads that there's nothing doing.
|
|
* Called at the end of a scene.
|
|
*/
|
|
void DropMovers() {
|
|
for (int i = 0; i < MAX_MOVERS; i++)
|
|
InitMover(&g_Movers[i]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Reposition a moving actor.
|
|
*/
|
|
void PositionMover(MOVER *pMover, int x, int y) {
|
|
int z;
|
|
int node;
|
|
HPOLYGON hPath;
|
|
|
|
assert(pMover); // Moving null moving actor
|
|
assert(pMover->actorObj);
|
|
|
|
pMover->objX = x;
|
|
pMover->objY = y;
|
|
MultiSetAniXY(pMover->actorObj, x, y);
|
|
|
|
hPath = InPolygon(x, y, PATH);
|
|
if (hPath != NOPOLY) {
|
|
pMover->hCpath = hPath;
|
|
if (PolySubtype(hPath) == NODE) {
|
|
node = NearestNodeWithin(hPath, x, y);
|
|
getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
|
|
pMover->hFnpath = hPath;
|
|
pMover->line = node;
|
|
pMover->npstatus = GOING_UP;
|
|
} else {
|
|
pMover->hFnpath = NOPOLY;
|
|
pMover->npstatus = NOT_IN;
|
|
}
|
|
|
|
z = GetScale(hPath, pMover->objY);
|
|
pMover->scale = z;
|
|
SetMoverStanding(pMover);
|
|
} else {
|
|
pMover->bNoPath = true;
|
|
|
|
pMover->hFnpath = NOPOLY; // Ain't in one
|
|
pMover->npstatus = NOT_IN;
|
|
|
|
// Ensure legal reel and scale
|
|
if (pMover->direction < 0 || pMover->direction > 3)
|
|
pMover->direction = FORWARD;
|
|
if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
|
|
pMover->scale = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get position of a moving actor.
|
|
*/
|
|
void GetMoverPosition(MOVER *pMover, int *paniX, int *paniY) {
|
|
assert(pMover); // Getting null moving actor's position
|
|
|
|
if (pMover->actorObj != NULL)
|
|
GetAniPosition(pMover->actorObj, paniX, paniY);
|
|
else {
|
|
*paniX = 0;
|
|
*paniY = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moving actor's mid-top position.
|
|
*/
|
|
void GetMoverMidTop(MOVER *pMover, int *aniX, int *aniY) {
|
|
assert(pMover); // Getting null moving actor's mid-top position
|
|
assert(pMover->actorObj); // Getting null moving actor's mid-top position
|
|
|
|
*aniX = (MultiLeftmost(pMover->actorObj) + MultiRightmost(pMover->actorObj)) / 2;
|
|
*aniY = MultiHighest(pMover->actorObj);
|
|
}
|
|
|
|
/**
|
|
* Moving actor's left-most co-ordinate.
|
|
*/
|
|
int GetMoverLeft(MOVER *pMover) {
|
|
assert(pMover); // Getting null moving actor's leftmost position
|
|
if (pMover->type == MOVER_3D) {
|
|
warning("TODO: Finish implementation of GetMoverLeft() for Noir");
|
|
return 0;
|
|
} else {
|
|
assert(pMover->actorObj); // Getting null moving actor's leftmost position
|
|
return MultiLeftmost(pMover->actorObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moving actor's right-most co-ordinate.
|
|
*/
|
|
int GetMoverRight(MOVER *pMover) {
|
|
assert(pMover); // Getting null moving actor's rightmost position
|
|
if (pMover->type == MOVER_3D) {
|
|
warning("TODO: Finish implementation of GetMoverRight() for Noir");
|
|
return 0;
|
|
} else {
|
|
assert(pMover->actorObj); // Getting null moving actor's rightmost position
|
|
return MultiRightmost(pMover->actorObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moving actor's top co-ordinate.
|
|
*/
|
|
int GetMoverTop(MOVER *pMover) {
|
|
assert(pMover); // Getting null moving actor's topmost position
|
|
|
|
if (pMover->type == MOVER_3D) {
|
|
warning("TODO: Finish implementation of GetMoverTop() for Noir");
|
|
return 0;
|
|
} else {
|
|
assert(pMover->actorObj); // Getting null moving actor's topmost position
|
|
return MultiHighest(pMover->actorObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Moving actor's bottom co-ordinate.
|
|
*/
|
|
int GetMoverBottom(MOVER *pMover) {
|
|
assert(pMover); // Getting null moving actor's bottommost position
|
|
if (pMover->type == MOVER_3D) {
|
|
warning("TODO: Finish implementation of GetMoverBottom() for Noir");
|
|
return 0;
|
|
} else {
|
|
assert(pMover->actorObj); // Getting null moving actor's bottommost position
|
|
return MultiLowest(pMover->actorObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* See if moving actor is stood within a polygon.
|
|
*/
|
|
bool MoverIsInPolygon(MOVER *pMover, HPOLYGON hp) {
|
|
assert(pMover); // Checking if null moving actor is in polygon
|
|
assert(pMover->actorObj); // Checking if null moving actor is in polygon
|
|
|
|
int aniX, aniY;
|
|
GetAniPosition(pMover->actorObj, &aniX, &aniY);
|
|
|
|
return IsInPolygon(aniX, aniY, hp);
|
|
}
|
|
|
|
/**
|
|
* Change which reel is playing for a moving actor.
|
|
*/
|
|
void AlterMover(MOVER *pMover, SCNHANDLE film, AR_FUNCTION fn) {
|
|
const FILM *pfilm;
|
|
|
|
assert(pMover->actorObj); // Altering null moving actor's animation script
|
|
|
|
if (fn == AR_POPREEL) {
|
|
// Use the saved film
|
|
film = pMover->hPushedFilm;
|
|
}
|
|
if (fn == AR_PUSHREEL) {
|
|
// Save the one we're replacing
|
|
pMover->hPushedFilm = (pMover->bSpecReel) ? pMover->hLastFilm : 0;
|
|
}
|
|
|
|
if (film == 0) {
|
|
if (pMover->bSpecReel) {
|
|
// Revert to 'normal' actor
|
|
SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
|
|
pMover->bSpecReel = false;
|
|
}
|
|
} else {
|
|
// Remember this one in case the actor talks
|
|
pMover->hLastFilm = film;
|
|
|
|
pfilm = (const FILM *)_vm->_handle->LockMem(film);
|
|
assert(pfilm != NULL);
|
|
|
|
InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate));
|
|
if (TinselVersion <= 1)
|
|
pMover->stepCount = 0;
|
|
|
|
// If no path, just use first path in the scene
|
|
if (pMover->hCpath != NOPOLY)
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
|
|
else
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
|
|
|
|
if (fn == AR_WALKREEL) {
|
|
pMover->bSpecReel = false;
|
|
pMover->bWalkReel = true;
|
|
} else {
|
|
pMover->bSpecReel = true;
|
|
pMover->bWalkReel = false;
|
|
|
|
#ifdef DEBUG
|
|
assert(StepAnimScript(&pMover->actorAnim) != ScriptFinished); // Actor reel has finished!
|
|
#else
|
|
StepAnimScript(&pMover->actorAnim); // 04/01/95
|
|
#endif
|
|
}
|
|
|
|
// Hang on, we may not want him yet! 04/01/95
|
|
if (pMover->bHidden)
|
|
MultiSetZPosition(pMover->actorObj, -1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the actor's direction.
|
|
*/
|
|
DIRECTION GetMoverDirection(MOVER *pMover) {
|
|
return pMover->direction;
|
|
}
|
|
|
|
/**
|
|
* Return the actor's scale.
|
|
*/
|
|
int GetMoverScale(MOVER *pMover) {
|
|
return pMover->scale;
|
|
}
|
|
|
|
/**
|
|
* Point actor in specified derection
|
|
*/
|
|
void SetMoverDirection(MOVER *pMover, DIRECTION dirn) {
|
|
pMover->direction = dirn;
|
|
}
|
|
|
|
/**
|
|
* Get actor to adopt its appropriate standing reel.
|
|
*/
|
|
void SetMoverStanding(MOVER *pMover) {
|
|
if (TinselVersion == 3) {
|
|
warning("TODO: Finish implementation of GetMoverStanding() for Noir");
|
|
return;
|
|
}
|
|
assert(pMover->actorObj);
|
|
AlterMover(pMover, pMover->standReels[pMover->scale - 1][pMover->direction], AR_NORMAL);
|
|
}
|
|
|
|
/**
|
|
* Get actor to adopt its appropriate walking reel.
|
|
*/
|
|
void SetMoverWalkReel(MOVER *pMover, DIRECTION reel, int scale, bool force) {
|
|
SCNHANDLE whichReel;
|
|
const FILM *pfilm;
|
|
|
|
// Kill off any play that may be going on for this actor
|
|
// and restore the real actor
|
|
_vm->_actor->storeActorReel(pMover->actorID, NULL, 0, NULL, 0, 0, 0);
|
|
UnHideMover(pMover);
|
|
|
|
// Don't do it if using a special walk reel
|
|
if (pMover->bWalkReel)
|
|
return;
|
|
|
|
if (force || pMover->scale != scale || pMover->direction != reel) {
|
|
assert(reel >= 0 && reel <= 3 && scale > 0 && scale <= TOTAL_SCALES); // out of range scale or reel
|
|
|
|
// If scale change and both are regular scales
|
|
// and there's a scaling reel in the right direction
|
|
if (pMover->scale != scale
|
|
&& scale <= NUM_MAINSCALES && pMover->scale <= NUM_MAINSCALES
|
|
&& (whichReel = ScalingReel(pMover->actorID, pMover->scale, scale, reel)) != 0) {
|
|
// error("Cripes");
|
|
; // Use what is now in 'whichReel'
|
|
} else {
|
|
whichReel = pMover->walkReels[scale-1][reel];
|
|
assert(whichReel); // no reel
|
|
}
|
|
|
|
pfilm = (const FILM *)_vm->_handle->LockMem(whichReel);
|
|
assert(pfilm != NULL); // no film
|
|
|
|
InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), 1);
|
|
|
|
// Synchronised walking reels
|
|
assert(pMover->stepCount >= 0);
|
|
SkipFrames(&pMover->actorAnim, pMover->stepCount);
|
|
|
|
pMover->scale = scale;
|
|
pMover->direction = reel;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sort some stuff out at actor start-up time.
|
|
*/
|
|
static void InitialPathChecks(MOVER *pMover, int xpos, int ypos) {
|
|
HPOLYGON hPath;
|
|
int node;
|
|
int z;
|
|
|
|
pMover->objX = xpos;
|
|
pMover->objY = ypos;
|
|
|
|
/*--------------------------------------
|
|
| If Actor is in a follow nodes path, |
|
|
| position it at the nearest node. |
|
|
--------------------------------------*/
|
|
hPath = InPolygon(xpos, ypos, PATH);
|
|
|
|
if (hPath != NOPOLY) {
|
|
pMover->hCpath = hPath;
|
|
if (PolySubtype(hPath) == NODE) {
|
|
node = NearestNodeWithin(hPath, xpos, ypos);
|
|
getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
|
|
pMover->hFnpath = hPath;
|
|
pMover->line = node;
|
|
pMover->npstatus = GOING_UP;
|
|
}
|
|
|
|
z = GetScale(hPath, pMover->objY);
|
|
} else {
|
|
pMover->bNoPath = true;
|
|
|
|
z = GetScale(FirstPathPoly(), pMover->objY);
|
|
}
|
|
if (TinselVersion == 3) {
|
|
warning("TODO: Finish implementation of InitialPathChecks() for Noir");
|
|
} else {
|
|
SetMoverWalkReel(pMover, FORWARD, z, false);
|
|
}
|
|
}
|
|
|
|
static void MoverProcessHelper(int X, int Y, int id, MOVER *pMover) {
|
|
const FILM *pfilm = (const FILM *)_vm->_handle->LockMem(pMover->walkReels[0][FORWARD]);
|
|
const MULTI_INIT *pmi = pfilm->reels[0].GetMultiInit();
|
|
|
|
assert(_vm->_bg->BgPal()); // Can't start actor without a background palette
|
|
assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels
|
|
|
|
InitMover(pMover);
|
|
InitialPathChecks(pMover, X, Y);
|
|
|
|
PokeInPalette(pmi);
|
|
|
|
pMover->actorObj = MultiInitObject(pmi);
|
|
|
|
assert(pMover->actorID == id);
|
|
pMover->actorID = id;
|
|
|
|
// add it to display list
|
|
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
|
|
_vm->_actor->storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0);
|
|
|
|
InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate));
|
|
pMover->stepCount = 0;
|
|
|
|
MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);
|
|
|
|
// If no path, just use first path in the scene
|
|
if (pMover->hCpath != NOPOLY)
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
|
|
else
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
|
|
|
|
// Make him the right size
|
|
SetMoverStanding(pMover);
|
|
|
|
//**** if added 18/11/94, am
|
|
if (X != MAGICX && Y != MAGICY) {
|
|
HideMover(pMover, 0); // Allows a play to come in before this appears
|
|
pMover->bHidden = false; // ...but don't stay hidden
|
|
}
|
|
|
|
pMover->bActive = true;
|
|
}
|
|
|
|
/**
|
|
* Moving actor process - 1 per moving actor in current scene.
|
|
*/
|
|
void T1MoverProcess(CORO_PARAM, const void *param) {
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
// FIXME: Code without typedef emits -Wcast-qual GCC warning.
|
|
// However, adding const casts break compilation with -fpermissive.
|
|
// Reverted to local typedef for now until this can be avoided.
|
|
#if 0
|
|
MOVER *pActor = *(MOVER **)param;
|
|
//const MOVER *pActor = *(const MOVER **)param;
|
|
#else
|
|
typedef MOVER *PMOVER;
|
|
const PMOVER pActor = *(const PMOVER *)param;
|
|
#endif
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
while (1) {
|
|
if (pActor->bSpecReel) {
|
|
if (!pActor->bHidden)
|
|
#ifdef DEBUG
|
|
assert(StepAnimScript(&pActor->actorAnim) != ScriptFinished); // Actor reel has finished!
|
|
#else
|
|
StepAnimScript(&pActor->actorAnim);
|
|
#endif
|
|
} else
|
|
DoMoveActor(pActor);
|
|
|
|
CORO_SLEEP(1); // allow rescheduling
|
|
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Tinsel 2 Moving actor process
|
|
* - 1 per moving actor in current scene.
|
|
*/
|
|
void T2MoverProcess(CORO_PARAM, const void *param) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
// Get the co-ordinates - copied to process when it was created
|
|
const MAINIT *rpos = (const MAINIT *)param;
|
|
MOVER *pMover = rpos->pMover;
|
|
int i;
|
|
FILM *pFilm;
|
|
const MULTI_INIT *pmi;
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
for (i = 0; i < TOTAL_SCALES; i++) {
|
|
if (pMover->walkReels[i][FORWARD])
|
|
break;
|
|
}
|
|
assert(i < TOTAL_SCALES);
|
|
|
|
InitMover(pMover);
|
|
InitialPathChecks(pMover, rpos->X, rpos->Y);
|
|
|
|
pFilm = (FILM *)_vm->_handle->LockMem(pMover->walkReels[i][FORWARD]); // Any old reel
|
|
pmi = pFilm->reels[0].GetMultiInit();
|
|
|
|
// Poke in the background palette
|
|
PokeInPalette(pmi);
|
|
|
|
pMover->actorObj = MultiInitObject(pmi);
|
|
// FIXME: This is what the original did. A bug, perhaps?
|
|
// pMover->actorID = pMover->actorID;
|
|
pMover->bActive = true;
|
|
|
|
// add it to display list
|
|
MultiInsertObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
|
|
|
|
InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate);
|
|
pMover->stepCount = 0;
|
|
|
|
MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);
|
|
|
|
// If no path, just use first path in the scene
|
|
if (pMover->hCpath != NOPOLY)
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
|
|
else
|
|
SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
|
|
|
|
// Make him the right size
|
|
SetMoverStanding(pMover);
|
|
|
|
HideMover(pMover); // Allows a play to come in before this appears
|
|
pMover->bHidden = false; // ...but don't stay hidden
|
|
|
|
for (;;) {
|
|
if (pMover->bSpecReel) {
|
|
if (!pMover->bHidden)
|
|
StepAnimScript(&pMover->actorAnim);
|
|
} else
|
|
DoMoveActor(pMover);
|
|
|
|
CheckBrightness(pMover);
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Tinsel 3 Moving actor process
|
|
* - 1 per moving actor in current scene.
|
|
*/
|
|
void T3MoverProcess(CORO_PARAM, const void *param) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
// Get the co-ordinates - copied to process when it was created
|
|
const MAINIT *rpos = (const MAINIT *)param;
|
|
MOVER *pMover = rpos->pMover;
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
warning("TODO: Finish implementation of T3MoverProcess() for Noir");
|
|
|
|
InitMover(pMover);
|
|
InitialPathChecks(pMover, rpos->X, rpos->Y);
|
|
|
|
HideMover(pMover); // Allows a play to come in before this appears
|
|
pMover->bHidden = false; // ...but don't stay hidden
|
|
|
|
for (;;) {
|
|
DoMoveActor(pMover);
|
|
|
|
CheckBrightness(pMover);
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a handling process for a moving actor
|
|
*/
|
|
void MoverProcessCreate(int X, int Y, int id, MOVER *pMover) {
|
|
if (TinselVersion >= 2) {
|
|
MAINIT iStruct;
|
|
iStruct.X = X;
|
|
iStruct.Y = Y;
|
|
iStruct.pMover = pMover;
|
|
|
|
CoroScheduler.createProcess(PID_MOVER, (TinselVersion == 3) ? T3MoverProcess : T2MoverProcess, &iStruct, sizeof(MAINIT));
|
|
} else {
|
|
MoverProcessHelper(X, Y, id, pMover);
|
|
pMover->pProc = CoroScheduler.createProcess(PID_MOVER, T1MoverProcess, &pMover, sizeof(MOVER *));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for moving actor collision.
|
|
*/
|
|
MOVER *InMoverBlock(MOVER *pMover, int x, int y) {
|
|
int caX; // Calling actor's pos'n
|
|
int caL, caR; // Calling actor's left and right
|
|
int taX, taY; // Test actor's pos'n
|
|
int taL, taR; // Test actor's left and right
|
|
|
|
caX = pMover->objX;
|
|
if (pMover->hFnpath != NOPOLY || GetNoBlocking())
|
|
return NULL;
|
|
|
|
caL = GetMoverLeft(pMover) + x - caX;
|
|
caR = GetMoverRight(pMover) + x - caX;
|
|
|
|
for (int i = 0; i < MAX_MOVERS; i++) {
|
|
if (pMover == &g_Movers[i] ||
|
|
((TinselVersion >= 2) && (g_Movers[i].actorObj == NULL)) ||
|
|
((TinselVersion <= 1) && !g_Movers[i].bActive))
|
|
continue;
|
|
|
|
// At around the same height?
|
|
GetMoverPosition(&g_Movers[i], &taX, &taY);
|
|
if (g_Movers[i].hFnpath != NOPOLY)
|
|
continue;
|
|
|
|
if (ABS(y - taY) > 2) // 2 was 8
|
|
continue;
|
|
|
|
// To the left?
|
|
taL = GetMoverLeft(&g_Movers[i]);
|
|
if (caR <= taL)
|
|
continue;
|
|
|
|
// To the right?
|
|
taR = GetMoverRight(&g_Movers[i]);
|
|
if (caL >= taR)
|
|
continue;
|
|
|
|
return &g_Movers[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Copies key information for savescn.c to store away.
|
|
*/
|
|
void SaveMovers(SAVED_MOVER *sMoverInfo) {
|
|
for (int i = 0; i < MAX_MOVERS; i++) {
|
|
sMoverInfo[i].bActive = (TinselVersion <= 1) ? g_Movers[i].bActive : g_Movers[i].actorObj != NULL;
|
|
sMoverInfo[i].actorID = g_Movers[i].actorID;
|
|
sMoverInfo[i].objX = g_Movers[i].objX;
|
|
sMoverInfo[i].objY = g_Movers[i].objY;
|
|
sMoverInfo[i].hLastfilm = g_Movers[i].hLastFilm;
|
|
|
|
if (TinselVersion >= 2) {
|
|
sMoverInfo[i].bHidden = g_Movers[i].bHidden;
|
|
sMoverInfo[i].brightness = g_Movers[i].brightness;
|
|
sMoverInfo[i].startColor = g_Movers[i].startColor;
|
|
sMoverInfo[i].paletteLength = g_Movers[i].paletteLength;
|
|
}
|
|
|
|
memcpy(sMoverInfo[i].walkReels, g_Movers[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
memcpy(sMoverInfo[i].standReels, g_Movers[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
memcpy(sMoverInfo[i].talkReels, g_Movers[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
}
|
|
}
|
|
|
|
void RestoreAuxScales(SAVED_MOVER *sMoverInfo) {
|
|
for (int i = 0; i < MAX_MOVERS; i++) {
|
|
if (TinselVersion >= 2)
|
|
g_Movers[i].actorID = sMoverInfo[i].actorID;
|
|
|
|
memcpy(g_Movers[i].walkReels, sMoverInfo[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
memcpy(g_Movers[i].standReels, sMoverInfo[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
memcpy(g_Movers[i].talkReels, sMoverInfo[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
|
|
}
|
|
}
|
|
|
|
|
|
MOVER *NextMover(MOVER *pMover) {
|
|
int next;
|
|
|
|
if (pMover == NULL)
|
|
next = 0;
|
|
else
|
|
next = pMover - g_Movers + 1;
|
|
|
|
if (g_Movers[next].actorID)
|
|
return &g_Movers[next];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
void StopMover(MOVER *pMover) {
|
|
pMover->bStop = true;
|
|
DoMoveActor(pMover);
|
|
}
|
|
|
|
} // End of namespace Tinsel
|