mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-22 04:01:23 +00:00

Renamed rince.* files to movers to be more game independent. Added elementary support for Noir movers which can use different logic. Allows game to boot to the first interactive scene, but there is no 3D model rendered (that is WIP).
1030 lines
26 KiB
C++
1030 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 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.
|
|
*
|
|
* 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(PMOVER pMover, int startColor, int length) {
|
|
pMover->startColor = startColor;
|
|
pMover->paletteLength = length;
|
|
}
|
|
|
|
/**
|
|
* Called from the moving actor's main loop.
|
|
*/
|
|
static void CheckBrightness(PMOVER 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(PMOVER 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.
|
|
*/
|
|
PMOVER 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.
|
|
*/
|
|
PMOVER 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.
|
|
*/
|
|
PMOVER 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(PMOVER pMover) {
|
|
if (pMover->bActive) {
|
|
pMover->bActive = false;
|
|
MultiDeleteObject(_vm->_bg->GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
|
|
pMover->actorObj = nullptr;
|
|
assert(CoroScheduler.getCurrentProcess() != pMover->pProc);
|
|
CoroScheduler.killProcess(pMover->pProc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* getMActorState
|
|
*/
|
|
bool getMActorState(PMOVER 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(PMOVER pMover, int sf) {
|
|
assert(pMover); // Hiding null moving actor
|
|
|
|
pMover->bHidden = true;
|
|
|
|
if (!TinselV2) {
|
|
// 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(PMOVER pMover) {
|
|
if (pMover)
|
|
return pMover->bHidden;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* To be or not to be? If it be, then it is.
|
|
*/
|
|
bool MoverIs(PMOVER pMover) {
|
|
if (TinselV2)
|
|
return pMover->actorObj ? true : false;
|
|
else
|
|
return getMActorState(pMover);
|
|
}
|
|
|
|
/**
|
|
* To be SWalk()ing or not to be SWalk()ing?
|
|
*/
|
|
bool MoverIsSWalking(PMOVER pMover) {
|
|
return (MoverMoving(pMover) && pMover->bIgPath);
|
|
}
|
|
|
|
/**
|
|
* MoverMoving()
|
|
*/
|
|
bool MoverMoving(PMOVER pMover) {
|
|
if (!TinselV2)
|
|
return pMover->bMoving;
|
|
|
|
if (pMover->UtargetX == -1 && pMover->UtargetY == -1)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return an actor's walk ticket.
|
|
*/
|
|
int GetWalkNumber(PMOVER pMover) {
|
|
return pMover->walkNumber;
|
|
}
|
|
|
|
/**
|
|
* GetMoverId
|
|
*/
|
|
int GetMoverId(PMOVER pMover) {
|
|
return pMover->actorID;
|
|
}
|
|
|
|
/**
|
|
* Sets the mover Z position
|
|
*/
|
|
void SetMoverZ(PMOVER pMover, int y, uint32 zFactor) {
|
|
if (!pMover->bHidden) {
|
|
if (!TinselV2)
|
|
_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(PMOVER pMover, uint32 zFactor) {
|
|
pMover->zOverride = zFactor;
|
|
}
|
|
|
|
/**
|
|
* UnHideMover
|
|
*/
|
|
void UnHideMover(PMOVER pMover) {
|
|
assert(pMover); // unHiding null moving actor
|
|
|
|
if (!TinselV2 || 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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(PMOVER 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 (!TinselV2)
|
|
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(PMOVER pMover) {
|
|
return pMover->direction;
|
|
}
|
|
|
|
/**
|
|
* Return the actor's scale.
|
|
*/
|
|
int GetMoverScale(PMOVER pMover) {
|
|
return pMover->scale;
|
|
}
|
|
|
|
/**
|
|
* Point actor in specified derection
|
|
*/
|
|
void SetMoverDirection(PMOVER pMover, DIRECTION dirn) {
|
|
pMover->direction = dirn;
|
|
}
|
|
|
|
/**
|
|
* Get actor to adopt its appropriate standing reel.
|
|
*/
|
|
void SetMoverStanding(PMOVER pMover) {
|
|
if (TinselV3) {
|
|
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(PMOVER 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(PMOVER 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 (TinselV3) {
|
|
warning("TODO: Finish implementation of InitialPathChecks() for Noir");
|
|
} else {
|
|
SetMoverWalkReel(pMover, FORWARD, z, false);
|
|
}
|
|
}
|
|
|
|
static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) {
|
|
const FILM *pfilm;
|
|
const MULTI_INIT *pmi;
|
|
const FRAME *pFrame;
|
|
IMAGE *pim;
|
|
|
|
|
|
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);
|
|
|
|
pfilm = (const FILM *)_vm->_handle->LockMem(pMover->walkReels[0][FORWARD]);
|
|
pmi = (const MULTI_INIT *)_vm->_handle->LockMem(FROM_32(pfilm->reels[0].mobj));
|
|
|
|
//---
|
|
pFrame = (const FRAME *)_vm->_handle->LockMem(FROM_32(pmi->hMulFrame));
|
|
|
|
// get pointer to image
|
|
pim = (IMAGE *)_vm->_handle->LockMem(READ_32(pFrame)); // handle to image
|
|
pim->hImgPal = TO_32(_vm->_bg->BgPal());
|
|
//---
|
|
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);
|
|
|
|
const PMOVER pActor = *(const PMOVER *)param;
|
|
|
|
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;
|
|
PMOVER pMover = rpos->pMover;
|
|
int i;
|
|
FILM *pFilm;
|
|
PMULTI_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 = (PMULTI_INIT)_vm->_handle->LockMem(FROM_32(pFilm->reels[0].mobj));
|
|
|
|
// 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;
|
|
PMOVER 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, PMOVER pMover) {
|
|
if (TinselV2 || TinselV3) {
|
|
MAINIT iStruct;
|
|
iStruct.X = X;
|
|
iStruct.Y = Y;
|
|
iStruct.pMover = pMover;
|
|
|
|
CoroScheduler.createProcess(PID_MOVER, TinselV3 ? T3MoverProcess : T2MoverProcess, &iStruct, sizeof(MAINIT));
|
|
} else {
|
|
MoverProcessHelper(X, Y, id, pMover);
|
|
pMover->pProc = CoroScheduler.createProcess(PID_MOVER, T1MoverProcess, &pMover, sizeof(PMOVER));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for moving actor collision.
|
|
*/
|
|
PMOVER InMoverBlock(PMOVER 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] ||
|
|
(TinselV2 && (g_Movers[i].actorObj == NULL)) ||
|
|
(!TinselV2 && !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 = !TinselV2 ? 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 (TinselV2) {
|
|
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 (TinselV2)
|
|
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));
|
|
}
|
|
}
|
|
|
|
|
|
PMOVER NextMover(PMOVER 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(PMOVER pMover) {
|
|
pMover->bStop = true;
|
|
DoMoveActor(pMover);
|
|
}
|
|
|
|
} // End of namespace Tinsel
|