scummvm/engines/tinsel/move.cpp
2011-05-25 10:50:46 -04:00

1748 lines
48 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.
*
* Handles walking and use of the path system.
*
* Contains the dodgiest code in the whole system.
*/
#include "tinsel/actors.h"
#include "tinsel/anim.h"
#include "tinsel/background.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/move.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/scroll.h"
#include "tinsel/tinlib.h" // For Stand()
#include "tinsel/tinsel.h"
namespace Tinsel {
//----------------- DEVELOPMENT OPTIONS --------------------
#define SLOW_RINCE_DOWN 0
//----------------- EXTERNAL FUNCTIONS ---------------------
// in POLYGONS.C
// Deliberatley defined here, and not in polygons.h
HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta);
//----------------- LOCAL DEFINES --------------------
#define XMDIST (TinselV2 ? 6 : 4)
#define XHMDIST (TinselV2 ? 3 : 2)
#define YMDIST (TinselV2 ? 3 : 2)
#define YHMDIST (TinselV2 ? 3 : 2)
#define XTHERE 1
#define XRESTRICT 2
#define YTHERE 4
#define YRESTRICT 8
#define STUCK 16
#define LEAVING_PATH 0x100
#define ENTERING_BLOCK 0x200
#define ENTERING_MBLOCK 0x400
#define ALL_SORTED 1
#define NOT_SORTED 0
#define STEPS_MAX (TinselV2 ? 12 : 6)
//----------------- LOCAL GLOBAL DATA --------------------
// FIXME: Avoid non-const global vars
#if SLOW_RINCE_DOWN
static int Interlude = 0; // For slowing down walking, for testing
static int BogusVar = 0; // For slowing down walking, for testing
#endif
static int32 DefaultRefer = 0;
static int lastLeadXdest = 0, lastLeadYdest = 0;
static int hSlowVar = 0; // used by MoveActor()
//----------------- FORWARD REFERENCES --------------------
static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
int *newx, int *newy, int *s1, int *s2, HPOLYGON *hS2p,
bool bOver, bool bBodge,
PMOVER pActor, PMOVER *collisionActor = 0);
#if SLOW_RINCE_DOWN
/**
* AddInterlude
*/
void AddInterlude(int n) {
Interlude += n;
if (Interlude < 0)
Interlude = 0;
}
#endif
/**
* Given (x, y) of a click within a path polygon, checks that the
* co-ordinates are not within a blocking polygon. If it is not, the
* destination is the click point, otherwise tries to find a legal point
* below or above the click point.
* Returns:
* NOT_SORTED - if a destination is worked out (movement required)
* ALL_SORTED - no destination found (so no movement required)
*/
static int ClickedOnPath(int clickX, int clickY, int *ptgtX, int *ptgtY) {
int Loffset, Toffset;
int i;
/*--------------------------------------
Clicked within a path,
go to where requested unless blocked.
--------------------------------------*/
if (InPolygon(clickX, clickY, BLOCK) == NOPOLY) {
// Not in a blocking polygon - go to where requested.
*ptgtX = clickX;
*ptgtY = clickY;
} else {
/*------------------------------------------------------
In a Blocking polygon - try searching down and up.
If still nowhere (for now) give up!
------------------------------------------------------*/
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
for (i = clickY+1; i < SCREEN_HEIGHT + Toffset; i++) {
// Don't leave the path system
if (InPolygon(clickX, i, PATH) == NOPOLY) {
i = SCREEN_HEIGHT;
break;
}
if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
}
}
if (i == SCREEN_HEIGHT) {
for (i = clickY-1; i >= Toffset; i--) {
// Don't leave the path system
if (InPolygon(clickX, i, PATH) == NOPOLY) {
i = -1;
break;
}
if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
}
}
}
if (i < 0) {
return ALL_SORTED;
}
}
return NOT_SORTED;
}
/**
* Given (x, y) of a click within a referral polygon, works out the
* destination according to the referral type.
* Returns:
* NOT_SORTED - if a destination is worked out (movement required)
* ALL_SORTED - no destination found (so no movement required)
*/
static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX, int *ptgtY) {
int i;
int end; // Extreme of the scene
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
*ptgtX = *ptgtY = -1;
switch (PolySubtype(hRefpoly)) {
case REF_POINT: // Go to specified node
GetPolyNode(hRefpoly, ptgtX, ptgtY);
assert(InPolygon(*ptgtX, *ptgtY, PATH) != NOPOLY); // POINT Referral to illegal point
break;
case REF_DOWN: // Search downwards
end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY
&& InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
}
break;
case REF_UP: // Search upwards
for (i = clickY-1; i >= 0; i--)
if (InPolygon(clickX, i, PATH) != NOPOLY
&& InPolygon(clickX, i, BLOCK) == NOPOLY) {
*ptgtX = clickX;
*ptgtY = i;
break;
}
break;
case REF_RIGHT: // Search to the right
end = BgWidth();
for (i = clickX+1; i < end; i++)
if (InPolygon(i, clickY, PATH) != NOPOLY
&& InPolygon(i, clickY, BLOCK) == NOPOLY) {
*ptgtX = i;
*ptgtY = clickY;
break;
}
break;
case REF_LEFT: // Search to the left
for (i = clickX-1; i >= 0; i--)
if (InPolygon(i, clickY, PATH) != NOPOLY
&& InPolygon(i, clickY, BLOCK) == NOPOLY) {
*ptgtX = i;
*ptgtY = clickY;
break;
}
break;
}
if (*ptgtX != -1 && *ptgtY != -1) {
return NOT_SORTED;
} else
return ALL_SORTED;
}
/**
* Given (x, y) of a click, works out the destination according to the
* default referral type.
* Returns:
* NOT_SORTED - if a destination is worked out (movement required)
* ALL_SORTED - no destination found (so no movement required)
*/
static int ClickedOnNothing(int clickX, int clickY, int *ptgtX, int *ptgtY) {
int i;
int end; // Extreme of the scene
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
switch (DefaultRefer) {
case REF_DEFAULT:
// Try searching down and up (onscreen).
for (i = clickY+1; i < SCREEN_HEIGHT+Toffset; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
for (i = clickY-1; i >= Toffset; i--)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
// Try searching down and up (offscreen).
end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
for (i = clickY-1; i >= 0; i--)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
break;
case REF_UP:
for (i = clickY-1; i >= 0; i--)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
break;
case REF_DOWN:
end = BgHeight();
for (i = clickY+1; i < end; i++)
if (InPolygon(clickX, i, PATH) != NOPOLY) {
return ClickedOnPath(clickX, i, ptgtX, ptgtY);
}
break;
case REF_LEFT:
for (i = clickX-1; i >= 0; i--)
if (InPolygon(i, clickY, PATH) != NOPOLY) {
return ClickedOnPath(i, clickY, ptgtX, ptgtY);
}
break;
case REF_RIGHT:
end = BgWidth();
for (i = clickX + 1; i < end; i++)
if (InPolygon(i, clickY, PATH) != NOPOLY) {
return ClickedOnPath(i, clickY, ptgtX, ptgtY);
}
break;
}
// Going nowhere!
return ALL_SORTED;
}
/**
* Given (x, y) of the click, ascertains whether the click is within a
* path, within a referral poly, or niether. The appropriate function
* then gets called to give us a revised destination.
* Returns:
* NOT_SORTED - if a destination is worked out (movement required)
* ALL_SORTED - no destination found (so no movement required)
*/
static int WorkOutDestination(int clickX, int clickY, int *ptgtX, int *ptgtY) {
HPOLYGON hPoly;
/*--------------------------------------
Clicked within a path?
if not, within a referral poly?
if not, try and sort something out.
---------------------------------------*/
if (InPolygon(clickX, clickY, PATH) != NOPOLY) {
return ClickedOnPath(clickX, clickY, ptgtX, ptgtY);
} else if ((hPoly = InPolygon(clickX, clickY, REFER)) != NOPOLY) {
return ClickedOnRefer(hPoly, clickX, clickY, ptgtX, ptgtY);
} else {
return ClickedOnNothing(clickX, clickY, ptgtX, ptgtY);
}
}
/**
* Work out which reel to adopt for a section of movement.
*/
DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
HPOLYGON hPath, YBIAS yBias) {
int xchange = 0, ychange = 0;
enum {X_NONE, X_LEFT, X_RIGHT, X_NO} xdir;
enum {Y_NONE, Y_UP, Y_DOWN, Y_NO} ydir;
DIRECTION reel = lastreel; // Leave alone if can't decide
/*
* Determine size and direction of X movement.
* i.e. left, right, none or not allowed.
*/
if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_VERT))
xdir = X_NO;
else if (tox == -1)
xdir = X_NONE;
else {
xchange = tox - fromx;
if (xchange > 0)
xdir = X_RIGHT;
else if (xchange < 0) {
xchange = -xchange;
xdir = X_LEFT;
} else
xdir = X_NONE;
}
/*
* Determine size and direction of Y movement.
* i.e. up, down, none or not allowed.
*/
if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_HORIZ))
ydir = Y_NO;
else if (toy == -1)
ydir = Y_NONE;
else {
ychange = toy - fromy;
if (ychange > 0)
ydir = Y_DOWN;
else if (ychange < 0) {
ychange = -ychange;
ydir = Y_UP;
} else
ydir = Y_NONE;
}
/*
* Some adjustment to allow for different x and y pixell sizes.
*/
switch (yBias) {
case YB_X2:
ychange += ychange; // Double y distance to cover
break;
case YB_X1_5:
ychange += ychange / 2; // Double y distance to cover
break;
}
/*
* Determine which reel to use.
*/
if (xdir == X_NO) {
// Forced to be FORWARD or AWAY
switch (ydir) {
case Y_DOWN:
reel = FORWARD;
break;
case Y_UP:
reel = AWAY;
break;
default:
if (reel != AWAY) // No gratuitous turn
reel = FORWARD;
break;
}
} else if (ydir == Y_NO) {
// Forced to be LEFTREEL or RIGHTREEL
switch (xdir) {
case X_LEFT:
reel = LEFTREEL;
break;
case X_RIGHT:
reel = RIGHTREEL;
break;
default:
if (reel != LEFTREEL) // No gratuitous turn
reel = RIGHTREEL;
break;
}
} else if (xdir != X_NONE || ydir != Y_NONE) {
if (xdir == X_NONE)
reel = (ydir == Y_DOWN) ? FORWARD : AWAY;
else if (ydir == Y_NONE)
reel = (xdir == X_LEFT) ? LEFTREEL : RIGHTREEL;
else {
bool DontBother = false;
if (xchange <= 4 && ychange <= 4) {
switch (reel) {
case LEFTREEL:
if (xdir == X_LEFT)
DontBother = true;
break;
case RIGHTREEL:
if (xdir == X_RIGHT)
DontBother = true;
break;
case FORWARD:
if (ydir == Y_DOWN)
DontBother = true;
break;
case AWAY:
if (ydir == Y_UP)
DontBother = true;
break;
}
}
if (!DontBother) {
if (xchange > ychange)
reel = (xdir == X_LEFT) ? LEFTREEL : RIGHTREEL;
else
reel = (ydir == Y_DOWN) ? FORWARD : AWAY;
}
}
}
return reel;
}
/**
* Haven't moved, look towards the cursor.
*/
static void GotThereWithoutMoving(PMOVER pActor) {
int curX, curY;
DIRECTION reel;
if (!pActor->bSpecReel) {
GetCursorXYNoWait(&curX, &curY, true);
reel = GetDirection(pActor->objX, pActor->objY, curX, curY, pActor->direction, pActor->hCpath);
if (reel != pActor->direction)
SetMoverWalkReel(pActor, reel, pActor->scale, false);
}
}
/**
* Arrived at final destination.
*/
static void GotThere(PMOVER pMover) {
pMover->targetX = pMover->targetY = -1; // 4/1/95
pMover->ItargetX = pMover->ItargetY = -1;
pMover->UtargetX = pMover->UtargetY = -1;
// Perhaps we have'nt moved.
if (pMover->objX == (int)pMover->walkedFromX && pMover->objY == (int)pMover->walkedFromY) {
// Got there without moving
if (!TinselV2)
GotThereWithoutMoving(pMover);
else if (!pMover->bSpecReel) {
// No tag reel, look at cursor
int curX, curY;
DIRECTION direction;
GetCursorXY(&curX, &curY, true);
direction = GetDirection(pMover->objX, pMover->objY,
curX, curY,
pMover->direction,
pMover->hCpath,
YB_X2);
if (direction != pMover->direction)
SetMoverWalkReel(pMover, direction, pMover->scale, false);
}
}
if (!TinselV2)
ReTagActor(pMover->actorID); // Tag allowed while stationary
SetMoverStanding(pMover);
pMover->bMoving = false;
if (TinselV2 && pMover->bIgPath && pMover->zOverride != -1
&& InPolygon(pMover->objX, pMover->objY, PATH) == NOPOLY)
// New feature for end-of-scene walk-outs
SetMoverZ(pMover, pMover->objY, pMover->zOverride);
}
enum cgt { GT_NOTL, GT_NOTB, GT_NOT2, GT_OK, GT_MAY };
/**
* Can we get straight there?
*/
static cgt CanGetThere(PMOVER pActor, int tx, int ty) {
int s1, s2; // s2 not used here!
HPOLYGON hS2p; // nor is s2p!
int nextx, nexty;
int targetX = tx;
int targetY = ty; // Ultimate destination
int x = pActor->objX;
int y = pActor->objY; // Present position
while (targetX != -1 || targetY != -1) {
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
&s1, &s2, &hS2p, pActor->over, false, pActor);
if (s1 == (XTHERE | YTHERE)) {
return GT_OK; // Can get there directly.
} else if (s1 == (XTHERE | YRESTRICT) || s1 == (YTHERE | XRESTRICT)) {
return GT_MAY; // Can't get there directly.
} else if (s1 & STUCK) {
if (s2 == LEAVING_PATH)
return GT_NOTL; // Can't get there.
else
return GT_NOTB; // Can't get there.
} else if (x == nextx && y == nexty) {
return GT_NOT2; // Can't get there.
}
x = nextx;
y = nexty;
}
return GT_MAY;
}
/**
* Set final destination.
*/
static void SetMoverUltDest(PMOVER pActor, int x, int y) {
pActor->UtargetX = x;
pActor->UtargetY = y;
pActor->hUpath = InPolygon(x, y, PATH);
assert(pActor->hUpath != NOPOLY || pActor->bIgPath); // Invalid ultimate destination
}
/**
* Set intermediate destination.
*
* If in final destination path, go straight to target.
* If in a neighboring path to the final destination, if the target path
* is a follow nodes path, head for the end node, otherwise head straight
* for the target.
* Otherwise, head towards the pseudo-center or end node of the first
* en-route path.
*/
static void SetMoverIntDest(PMOVER pMover, int x, int y) {
HPOLYGON hIpath, hTpath;
int node;
hTpath = InPolygon(x, y, PATH); // Target path
#ifdef DEBUG
if (!pMover->bIgPath)
assert(hTpath != NOPOLY); // SetMoverIntDest() - target not in path
#endif
if (pMover->hCpath == hTpath || pMover->bIgPath
|| IsInPolygon(pMover->objX, pMover->objY, hTpath)) {
// In destination path - head straight for the target.
pMover->ItargetX = x;
pMover->ItargetY = y;
// make damn sure that Itarget is in hIpath
pMover->hIpath = !TinselV2 ? hTpath : InPolygon(x, y, PATH);
} else if (IsAdjacentPath(pMover->hCpath, hTpath)) {
// In path adjacent to target
if (PolySubtype(hTpath) != NODE) {
// Target path is normal - head for target.
// Added 26/01/95, innroom
if (CanGetThere(pMover, x, y) == GT_NOTL) {
NearestCorner(&x, &y, pMover->hCpath, hTpath);
}
pMover->ItargetX = x;
pMover->ItargetY = y;
if (TinselV2)
// make damn sure that Itarget is in hIpath
pMover->hIpath = InPolygon(x, y, PATH);
} else {
// Target path is node - head for end node.
node = NearestEndNode(hTpath, pMover->objX, pMover->objY);
getNpathNode(hTpath, node, &pMover->ItargetX, &pMover->ItargetY);
if (TinselV2)
// make damn sure that Itarget is in hIpath
pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
if (!TinselV2)
pMover->hIpath = hTpath;
} else {
assert(hTpath != NOPOLY); // Error 701
hIpath = GetPathOnTheWay(pMover->hCpath, hTpath);
if (TinselV2 && (hIpath == NOPOLY)) {
pMover->hIpath = NOPOLY;
} else if (hIpath != NOPOLY) {
/* Head for an en-route path */
if (PolySubtype(hIpath) != NODE) {
/* En-route path is normal - head for pseudo center. */
if (CanGetThere(pMover, x, y) == GT_OK) {
pMover->ItargetX = x;
pMover->ItargetY = y;
if (TinselV2)
// make damn sure that Itarget is in hIpath
pMover->hIpath = InPolygon(x, y, PATH);
} else {
pMover->ItargetX = PolyCenterX(hIpath);
pMover->ItargetY = PolyCenterY(hIpath);
if (TinselV2)
// make damn sure that Itarget is in hIpath
pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
} else {
/* En-route path is node - head for end node. */
node = NearestEndNode(hIpath, pMover->objX, pMover->objY);
getNpathNode(hIpath, node, &pMover->ItargetX, &pMover->ItargetY);
if (TinselV2)
// make damn sure that Itarget is in hIpath
pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
}
if (!TinselV2)
pMover->hIpath = hIpath;
}
}
pMover->InDifficulty = NO_PROB;
}
/**
* Set short-term destination and adopt the appropriate reel.
*/
static void SetMoverDest(PMOVER pActor, int x, int y) {
int scale;
DIRECTION reel;
// Set the co-ordinates requested.
pActor->targetX = x;
pActor->targetY = y;
pActor->InDifficulty = NO_PROB;
reel = GetDirection(pActor->objX, pActor->objY, x, y, pActor->direction, pActor->hCpath);
scale = GetScale(pActor->hCpath, pActor->objY);
if (scale != pActor->scale || reel != pActor->direction) {
SetMoverWalkReel(pActor, reel, scale, false);
}
}
/**
* SetNextDest
*/
static void SetNextDest(PMOVER pMover) {
int targetX, targetY; // Ultimate destination
int x, y; // Present position
int nextx, nexty;
int s1, lstatus = 0;
int s2;
HPOLYGON hS2p;
int i;
HPOLYGON hNpoly;
HPOLYGON hPath;
int znode;
int nx, ny;
int sx, sy;
HPOLYGON hEb;
int ss1, ss2;
HPOLYGON shS2p;
PMOVER collisionActor;
#if 1
int sTargetX, sTargetY;
#endif
/*
* Desired destination (Itarget) is already set
*/
x = pMover->objX; // Current position
y = pMover->objY;
targetX = pMover->ItargetX; // Desired position
targetY = pMover->ItargetY;
/*
* If we're where we're headed, end it all (the moving).
*/
// if (x == targetX && y == targetY)
if (ABS(x - targetX) < XMDIST && ABS(y - targetY) < YMDIST) {
if (targetX == pMover->UtargetX && targetY == pMover->UtargetY) {
// Desired position
GotThere(pMover);
return;
} else {
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5001
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
}
}
if (pMover->bNoPath || pMover->bIgPath) {
/* Can get there directly. */
SetMoverDest(pMover, targetX, targetY);
pMover->over = false;
return;
}
/*----------------------------------------------------------------------
| Some work to do here if we're in a follow nodes polygon - basically
| head for the next node.
----------------------------------------------------------------------*/
hNpoly = pMover->hFnpath; // The node path we're in (if any)
switch (pMover->npstatus) {
case NOT_IN:
break;
case ENTERING:
znode = NearestEndNode(hNpoly, x, y);
/* Hang on, we're probably here already! */
if (znode) {
pMover->npstatus = GOING_DOWN;
pMover->line = znode-1;
getNpathNode(hNpoly, znode - 1, &nx, &ny);
} else {
pMover->npstatus = GOING_UP;
pMover->line = znode;
getNpathNode(hNpoly, 1, &nx, &ny);
}
SetMoverDest(pMover, nx, ny);
// Test for pseudo-one-node npaths
if (numNodes(hNpoly) == 2 &&
ABS(pMover->objX - pMover->targetX) < XMDIST &&
ABS(pMover->objY - pMover->targetY) < YMDIST) {
// That's enough, we're leaving
pMover->npstatus = LEAVING;
} else {
// Normal situation
pMover->over = true;
return;
}
// Fall through for LEAVING
case LEAVING:
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5002
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
targetX = pMover->ItargetX; // Desired position
targetY = pMover->ItargetY;
break;
case GOING_UP:
i = pMover->line; // The line we're on
// Is this the final target line?
if (i+1 == pMover->Tline && hNpoly == pMover->hUpath) {
// The final leg of the journey
pMover->line = i+1;
SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
pMover->over = false;
return;
} else {
// Go to the next node unless we're at the last one
i++; // The node we're at
if (++i < numNodes(hNpoly)) {
getNpathNode(hNpoly, i, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->line = i-1;
if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
pMover->over = false;
else
pMover->over = true;
return;
} else {
// Last node - we're off
pMover->npstatus = LEAVING;
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5003
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
targetX = pMover->ItargetX; // Desired position
targetY = pMover->ItargetY;
break;
}
}
case GOING_DOWN:
i = pMover->line; // The line we're on and the node we're at
// Is this the final target line?
if (i - 1 == pMover->Tline && hNpoly == pMover->hUpath) {
// The final leg of the journey
SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
pMover->line = i-1;
pMover->over = false;
return;
} else {
// Go to the next node unless we're at the last one
if (--i >= 0) {
getNpathNode(hNpoly, i, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->line--; /* The next node to head for */
if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
pMover->over = false;
else
pMover->over = true;
return;
} else {
// Last node - we're off
pMover->npstatus = LEAVING;
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5004
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
targetX = pMover->ItargetX; // Desired position
targetY = pMover->ItargetY;
break;
}
}
}
/*------------------------------------------------------
| See if it can get there directly. There may be an
| intermediate destination to head for.
------------------------------------------------------*/
while (targetX != -1 || targetY != -1) {
#if 1
// 'push' the target
sTargetX = targetX;
sTargetY = targetY;
#endif
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
&s1, &s2, &hS2p, pMover->over, false, pMover, &collisionActor);
if (s1 != (XTHERE | YTHERE) && x == nextx && y == nexty) {
ss1 = s1;
ss2 = s2;
shS2p = hS2p;
#if 1
// 'pop' the target
targetX = sTargetX;
targetY = sTargetY;
#endif
// Note: this aint right - targetX/Y (may) have been
// nobbled by that last call to NewCoOrdinates()
// Re-instating them (can) leads to oscillation
NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
&s1, &s2, &hS2p, pMover->over, true, pMover, &collisionActor);
if (x == nextx && y == nexty) {
s1 = ss1;
s2 = ss2;
hS2p = shS2p;
}
}
if (s1 == (XTHERE | YTHERE)) {
/* Can get there directly. */
SetMoverDest(pMover, nextx, nexty);
pMover->over = false;
break;
} else if ((s1 & STUCK) || s1 == (XRESTRICT + YRESTRICT)
|| s1 == (XTHERE | YRESTRICT) || s1 == (YTHERE | XRESTRICT)) {
/*-------------------------------------------------
Can't go any further in this direction. |
If it's because of a blocking polygon, try to do |
something about it. |
-------------------------------------------------*/
if (s2 & ENTERING_BLOCK) {
x = pMover->objX; // Current position
y = pMover->objY;
// Go to the nearest corner of the blocking polygon concerned
BlockingCorner(hS2p, &x, &y, pMover->ItargetX, pMover->ItargetY);
SetMoverDest(pMover, x, y);
pMover->over = false;
} else if (s2 & ENTERING_MBLOCK) {
if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
// The best we're going to achieve
SetMoverUltDest(pMover, x, y);
SetMoverDest(pMover, x, y);
} else {
sx = pMover->objX;
sy = pMover->objY;
// pMover->objX = x;
// pMover->objY = y;
hEb = InitExtraBlock(pMover, collisionActor);
x = pMover->objX;
y = pMover->objY;
BlockingCorner(hEb, &x, &y, pMover->ItargetX, pMover->ItargetY);
pMover->objX = sx;
pMover->objY = sy;
SetMoverDest(pMover, x, y);
pMover->over = false;
}
} else {
/*----------------------------------------
Currently, this is as far as we can go. |
Definitely room for improvement here! |
----------------------------------------*/
hPath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
if (hPath != pMover->hIpath) {
if (IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hIpath))
hPath = pMover->hIpath;
}
assert(hPath == pMover->hIpath);
if (pMover->InDifficulty == NO_PROB) {
x = PolyCenterX(hPath);
y = PolyCenterY(hPath);
SetMoverDest(pMover, x, y);
pMover->InDifficulty = TRY_CENTER;
pMover->over = false;
} else if (pMover->InDifficulty == TRY_CENTER) {
NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
SetMoverDest(pMover, x, y);
pMover->InDifficulty = TRY_CORNER;
pMover->over = false;
} else if (pMover->InDifficulty == TRY_CORNER) {
NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
SetMoverDest(pMover, x, y);
pMover->InDifficulty = TRY_NEXTCORNER;
pMover->over = false;
}
}
break;
}
else if (((lstatus & YRESTRICT) && !(s1 & YRESTRICT))
|| ((lstatus & XRESTRICT) && !(s1 & XRESTRICT))) {
/*-----------------------------------------------
A restriction in a direction has been removed. |
Use this as an intermediate destination. |
-----------------------------------------------*/
SetMoverDest(pMover, nextx, nexty);
pMover->over = false;
break;
}
x = nextx;
y = nexty;
if (!TinselV2) {
/*-------------------------
Change of path polygon? |
-------------------------*/
hPath = InPolygon(x, y, PATH);
if (pMover->hCpath != hPath &&
!IsInPolygon(x, y, pMover->hCpath) &&
!IsAdjacentPath(pMover->hCpath, pMover->hIpath)) {
/*----------------------------------------------------------
If just entering a follow nodes polygon, go to first node.|
Else if just going to pass through, go to pseudo-center. |
----------------------------------------------------------*/
if (PolySubtype(hPath) == NODE && pMover->hFnpath != hPath && pMover->npstatus != LEAVING) {
int node = NearestEndNode(hPath, x, y);
getNpathNode(hPath, node, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->over = true;
} else if (!IsInPolygon(pMover->ItargetX, pMover->ItargetY, hPath) &&
!IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hCpath)) {
SetMoverDest(pMover, PolyCenterX(hPath), PolyCenterY(hPath));
pMover->over = true;
} else {
SetMoverDest(pMover, pMover->ItargetX, pMover->ItargetY);
}
break;
}
lstatus = s1;
}
}
}
/**
* Work out where the next position should be.
* Check that it's in a path and not in a blocking polygon.
*/
static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
int *newx, int *newy, int *s1, int *s2,
HPOLYGON *hS2p, bool bOver, bool bBodge,
PMOVER pMover, PMOVER *collisionActor) {
HPOLYGON hPoly;
int sidem, depthm;
int sidesteps, depthsteps;
PMOVER ma;
*s1 = *s2 = 0;
/*------------------------------------------------
Don't overrun if this is the final destination. |
------------------------------------------------*/
if ((*targetX == pMover->UtargetX && (*targetY == -1 || *targetY == pMover->UtargetY)) ||
(*targetY == pMover->UtargetY && (*targetX == -1 || *targetX == pMover->UtargetX)))
bOver = false;
/*----------------------------------------------------
Decide how big a step to attempt in each direction. |
----------------------------------------------------*/
sidesteps = *targetX == -1 ? 0 : *targetX - fromx;
sidesteps = ABS(sidesteps);
depthsteps = *targetY == -1 ? 0 : *targetY - fromy;
depthsteps = ABS(depthsteps);
if (sidesteps && depthsteps > sidesteps) {
depthm = YMDIST;
sidem = depthm * sidesteps/depthsteps;
if (!sidem)
sidem = 1;
} else if (depthsteps && sidesteps > depthsteps) {
sidem = XMDIST;
depthm = sidem * depthsteps/sidesteps;
if (!depthm) {
if (bBodge)
depthm = 1;
} else if (depthm > YMDIST)
depthm = YMDIST;
} else {
sidem = sidesteps ? XMDIST : 0;
depthm = depthsteps ? YMDIST : 0;
}
*newx = fromx;
*newy = fromy;
/*------------------------------------------------------------
If Left-Right movement is required - then make the move, |
but don't overshoot, and do notice when we're already there |
------------------------------------------------------------*/
if (*targetX == -1)
*s1 |= XTHERE;
else {
if (*targetX > fromx) { /* To the right? */
*newx += sidem; // Move to the right...
if (*newx == *targetX)
*s1 |= XTHERE;
else if (*newx > *targetX) { // ...but don't overshoot
if (!bOver)
*newx = *targetX;
else
*targetX = *newx;
*s1 |= XTHERE;
}
} else if (*targetX < fromx) { /* To the left? */
*newx -= sidem; // Move to the left...
if (*newx == *targetX)
*s1 |= XTHERE;
else if (*newx < *targetX) { // ...but don't overshoot
if (!bOver)
*newx = *targetX;
else
*targetX = *newx;
*s1 |= XTHERE;
}
} else {
*targetX = -1; // We're already there!
*s1 |= XTHERE;
}
}
/*--------------------------------------------------------------
If Up-Down movement is required - then make the move,
but don't overshoot, and do notice when we're already there
--------------------------------------------------------------*/
if (*targetY == -1)
*s1 |= YTHERE;
else {
if (*targetY > fromy) { /* Downwards? */
*newy += depthm; // Move down...
if (*newy == *targetY) // ...but don't overshoot
*s1 |= YTHERE;
else if (*newy > *targetY) { // ...but don't overshoot
if (!bOver)
*newy = *targetY;
else
*targetY = *newy;
*s1 |= YTHERE;
}
} else if (*targetY < fromy) { /* Upwards? */
*newy -= depthm; // Move up...
if (*newy == *targetY) // ...but don't overshoot
*s1 |= YTHERE;
else if (*newy < *targetY) { // ...but don't overshoot
if (!bOver)
*newy = *targetY;
else
*targetY = *newy;
*s1 |= YTHERE;
}
} else {
*targetY = -1; // We're already there!
*s1 |= YTHERE;
}
}
/* Give over if this is it */
if (*s1 == (XTHERE | YTHERE))
return;
/*------------------------------------------------------
Have worked out where an optimum step would take us.
Must now check if it's in a legal spot.
------------------------------------------------------*/
if (!pMover->bNoPath && !pMover->bIgPath) {
/*------------------------------
Must stay in a path polygon.
-------------------------------*/
hPoly = InPolygon(*newx, *newy, PATH);
if (hPoly == NOPOLY) {
*s2 = LEAVING_PATH; // Trying to leave the path polygons
if (*newx != fromx && InPolygon(*newx, fromy, PATH) != NOPOLY && InPolygon(*newx, fromy, BLOCK) == NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
} else if (*newy != fromy && InPolygon(fromx, *newy, PATH) != NOPOLY && InPolygon(fromx, *newy, BLOCK) == NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
*newx = fromx;
*newy = fromy;
#if 1
*targetX = *targetY = -1;
#endif
*s1 |= STUCK;
return;
}
}
/*--------------------------------------
Must stay out of blocking polygons.
--------------------------------------*/
hPoly = InPolygon(*newx, *newy, BLOCK);
if (hPoly != NOPOLY) {
*s2 = ENTERING_BLOCK; // Trying to enter a blocking poly
*hS2p = hPoly;
if (*newx != fromx && InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
} else if (*newy != fromy && InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
*newx = fromx;
*newy = fromy;
#if 1
*targetX = *targetY = -1;
#endif
*s1 |= STUCK;
}
}
/*------------------------------------------------------
Must stay out of moving actors' blocking polygons.
------------------------------------------------------*/
ma = InMoverBlock(pMover, *newx, *newy);
if (ma != NULL) {
// Ignore if already in it (it may have just appeared)
if (!InMoverBlock(pMover, pMover->objX, pMover->objY)) {
*s2 = ENTERING_MBLOCK; // Trying to walk through an actor
*hS2p = -1;
if (collisionActor)
*collisionActor = ma;
if (*newx != fromx && InMoverBlock(pMover, *newx, fromy) == NULL
&& InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
*newy = fromy;
*s1 |= YRESTRICT;
} else if (*newy != fromy && InMoverBlock(pMover, fromx, *newy) == NULL
&& InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
*newx = fromx;
*s1 |= XRESTRICT;
} else {
*newx = fromx;
*newy = fromy;
#if 1
*targetX = *targetY = -1;
#endif
*s1 |= STUCK;
}
}
}
}
}
/**
* SetOffWithinNodePath
*/
static void SetOffWithinNodePath(PMOVER pMover, HPOLYGON StartPath, HPOLYGON DestPath,
int targetX, int targetY) {
int endnode;
HPOLYGON hIpath;
int nx, ny;
int x, y;
if (StartPath == DestPath) {
if (pMover->line == pMover->Tline) {
SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
pMover->over = false;
} else if (pMover->line < pMover->Tline) {
getNpathNode(StartPath, pMover->line+1, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->npstatus = GOING_UP;
} else if (pMover->line > pMover->Tline) {
getNpathNode(StartPath, pMover->line, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->npstatus = GOING_DOWN;
}
} else {
/*
* Leaving this path - work out
* which end of this path to head for.
*/
assert(DestPath != NOPOLY); // Error 702
if ((hIpath = GetPathOnTheWay(StartPath, DestPath)) == NOPOLY) {
// This should never happen!
// It's the old code that didn't always work.
endnode = NearestEndNode(StartPath, targetX, targetY);
} else {
if (PolySubtype(hIpath) != NODE) {
x = PolyCenterX(hIpath);
y = PolyCenterY(hIpath);
endnode = NearestEndNode(StartPath, x, y);
} else {
endnode = NearEndNode(StartPath, hIpath);
}
}
#if 1
if ((pMover->npstatus == LEAVING) &&
endnode == NearestEndNode(StartPath, pMover->objX, pMover->objY)) {
// Leave it be
if (TinselV2) {
// Yeah, but we need a destination
// It's release night and there's this problem in the bar...
if (hIpath) // must be, but...
{
// could go for its end node if it's an NPATH
// but we probably will when we hit it anyway!
SetMoverDest(pMover, PolyCenterX(hIpath), PolyCenterY(hIpath));
}
}
} else
#endif
{
if (endnode) {
getNpathNode(StartPath, pMover->line+1, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->npstatus = GOING_UP;
} else {
getNpathNode(StartPath, pMover->line, &nx, &ny);
SetMoverDest(pMover, nx, ny);
pMover->npstatus = GOING_DOWN;
}
}
}
}
/**
* Restore a movement, called from restoreMovement() in ACTORS.CPP
*/
void SSetActorDest(PMOVER pActor) {
if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
Stand(nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
SetActorDest(pActor, pActor->UtargetX, pActor->UtargetY,
pActor->bIgPath, 0);
}
} else {
Stand(nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
}
}
/**
* Initiate a movement, called from WalkTo_Event()
*/
int SetActorDest(PMOVER pMover, int clickX, int clickY, bool igPath, SCNHANDLE hFilm) {
HPOLYGON StartPath, DestPath = 0;
int targetX, targetY;
if (TinselV2) {
// No need to synchronise if not moving!
// Hopefully will stop luggage flip in shades.
if (!MoverMoving(pMover))
pMover->stepCount = 0;
// Fix interrupted-walking-to-wardrobe bug in mortuary
StopMover(pMover);
} else {
if (pMover->actorID == GetLeadId()) // Now only for lead actor
UnTagActor(pMover->actorID); // Tag not allowed while moving
}
pMover->walkNumber++;
pMover->bStop = false;
pMover->over = false;
pMover->walkedFromX = pMover->objX;
pMover->walkedFromY = pMover->objY;
pMover->bMoving = true;
pMover->bIgPath = igPath;
pMover->zOverride = -1;
pMover->hRpath = NOPOLY;
if (!TinselV2) {
// Use the supplied reel or restore the normal actor.
if (hFilm != 0)
AlterMover(pMover, hFilm, AR_WALKREEL);
else
AlterMover(pMover, 0, AR_NORMAL);
}
if (igPath) {
targetX = clickX;
targetY = clickY;
if (pMover->actorID == GetLeadId()) {
lastLeadXdest = targetX;
lastLeadYdest = targetY;
}
} else {
int wodResult = WorkOutDestination(clickX, clickY, &targetX, &targetY);
if (pMover->actorID == GetLeadId()) {
lastLeadXdest = targetX;
lastLeadYdest = targetY;
}
if (wodResult == ALL_SORTED) {
GotThere(pMover);
return 0;
}
assert(InPolygon(targetX, targetY, PATH) != NOPOLY); // illegal destination!
assert(InPolygon(targetX, targetY, BLOCK) == NOPOLY); // illegal destination!
}
/***** Now have a destination to aim for. *****/
/*----------------------------------
| Don't move if it's not worth it.
----------------------------------*/
if (ABS(targetX - pMover->objX) < XMDIST && ABS(targetY - pMover->objY) < YMDIST) {
GotThere(pMover);
return 0;
}
/*------------------------------------------------------
| If the destiation is within a follow nodes polygon,
| set destination as the nearest node.
------------------------------------------------------*/
if (!igPath) {
DestPath = InPolygon(targetX, targetY, PATH);
if (PolySubtype(DestPath) == NODE) {
// Find the nearest point on a line, or nearest node
FindBestPoint(DestPath, &targetX, &targetY, &pMover->Tline);
}
}
assert(pMover->bIgPath || InPolygon(targetX, targetY, PATH) != NOPOLY); // Error 5005
SetMoverUltDest(pMover, targetX, targetY);
SetMoverIntDest(pMover, targetX, targetY);
if (TinselV2) {
// No movement for unconnected paths
if (pMover->hIpath == NOPOLY && !igPath) {
GotThere(pMover);
return 0;
}
// Use the supplied reel or restore the normal actor.
if (hFilm != 0)
AlterMover(pMover, hFilm, AR_WALKREEL);
else
AlterMover(pMover, 0, AR_NORMAL);
}
/*-------------------------------------------------------------------
| If in a follow nodes path, need to set off in the right direction! |
-------------------------------------------------------------------*/
if ((StartPath = pMover->hFnpath) != NOPOLY && !igPath) {
SetOffWithinNodePath(pMover, StartPath, DestPath, targetX, targetY);
} else {
// Set off!
SetNextDest(pMover);
}
return pMover->walkNumber;
}
/**
* Change scale if appropriate.
*/
static void CheckScale(PMOVER pActor, HPOLYGON hPath, int ypos) {
int scale;
scale = GetScale(hPath, ypos);
if (scale != pActor->scale) {
SetMoverWalkReel(pActor, pActor->direction, scale, false);
}
}
/**
* Not going anywhere - Kick off again if not at final destination.
*/
static void NotMoving(PMOVER pActor, int x, int y) {
pActor->targetX = pActor->targetY = -1;
// if (x == pActor->UtargetX && y == pActor->UtargetY)
if (ABS(x - pActor->UtargetX) < XMDIST && ABS(y - pActor->UtargetY) < YMDIST) {
GotThere(pActor);
return;
}
if (pActor->ItargetX != -1 || pActor->ItargetY != -1) {
SetNextDest(pActor);
} else if (pActor->UtargetX != -1 || pActor->UtargetY != -1) {
assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5006
SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
SetNextDest(pActor);
}
}
/**
* Does the necessary business when entering a different path polygon.
*/
static void EnteringNewPath(PMOVER pMover, HPOLYGON hPath, int x, int y) {
int firstnode; // First node to go to
int lastnode; // Last node to go to
HPOLYGON hIpath;
HPOLYGON hLpath; // one we're leaving
int nx, ny;
int nxl, nyl;
hLpath = pMover->hCpath;
pMover->hCpath = hPath; // current path
if (hPath == NOPOLY) {
// Not proved this ever happens, but just in case
pMover->hFnpath = NOPOLY;
pMover->npstatus = NOT_IN;
return;
}
// Is new path a node path?
if (PolySubtype(hPath) == NODE) {
// Node path - usually go to nearest end node
firstnode = NearestEndNode(hPath, x, y);
lastnode = -1;
// If this is not the destination path,
// find which end nodfe we wish to leave via
if (hPath != pMover->hUpath) {
if (pMover->bIgPath) {
lastnode = NearestEndNode(hPath, pMover->UtargetX, pMover->UtargetY);
} else {
assert(pMover->hUpath != NOPOLY); // Error 703
hIpath = GetPathOnTheWay(hPath, pMover->hUpath);
assert(hIpath != NOPOLY); // No path on the way
if (PolySubtype(hIpath) != NODE) {
lastnode = NearestEndNode(hPath, PolyCenterX(hIpath), PolyCenterY(hIpath));
} else {
lastnode = NearEndNode(hPath, hIpath);
}
}
}
// Test for pseudo-one-node npaths
if (lastnode != -1 && numNodes(hPath) == 2) {
getNpathNode(hPath, firstnode, &nx, &ny);
getNpathNode(hPath, lastnode, &nxl, &nyl);
if (nxl == nx && nyl == ny)
firstnode = lastnode;
}
// If leaving by same node as entering, don't bother.
if (lastnode == firstnode) {
pMover->hFnpath = NOPOLY;
pMover->npstatus = NOT_IN;
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5007
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
SetNextDest(pMover);
} else {
// Head for first node
pMover->over = true;
pMover->npstatus = ENTERING;
pMover->hFnpath = hPath;
pMover->line = firstnode ? firstnode - 1 : firstnode;
if (pMover->line == pMover->Tline && hPath == pMover->hUpath) {
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5008
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
} else {
// This doesn't seem right
getNpathNode(hPath, firstnode, &nx, &ny);
if (ABS(pMover->objX - nx) < XMDIST
&& ABS(pMover->objY - ny) < YMDIST) {
pMover->npstatus = ENTERING;
pMover->hFnpath = hPath;
SetNextDest(pMover);
} else {
getNpathNode(hPath, firstnode, &nx, &ny);
SetMoverDest(pMover, nx, ny);
}
}
}
return;
} else {
pMover->hFnpath = NOPOLY;
pMover->npstatus = NOT_IN;
assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5009
// Added 26/01/95
if (IsPolyCorner(hPath, pMover->ItargetX, pMover->ItargetY))
return;
// Added 23/10/96
if (TinselV2 && (pMover->hRpath == hPath))
return;
if (TinselV2)
pMover->hRpath = hLpath;
SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
SetNextDest(pMover);
}
}
/**
* Move
*/
void Move(PMOVER pMover, int newx, int newy, HPOLYGON hPath) {
pMover->objX = newx;
pMover->objY = newy;
MultiSetAniXY(pMover->actorObj, newx, newy);
SetMoverZ(pMover, newy, GetPolyZfactor(hPath));
if (StepAnimScript(&pMover->actorAnim) == ScriptFinished) {
// The end of a scale-change reel
// Revert to normal walking reel
pMover->bWalkReel = false;
pMover->stepCount = 0;
SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
}
// Synchronised walking reels
if (++pMover->stepCount >= STEPS_MAX)
pMover->stepCount = 0;
}
/**
* Called from MActorProcess() on every tick.
*
* Moves the actor as appropriate.
*/
void MoveActor(PMOVER pMover) {
int newx, newy;
HPOLYGON hPath;
int status, s2; // s2 not used here!
HPOLYGON hS2p; // nor is s2p!
HPOLYGON hEb;
PMOVER ma;
int sTargetX, sTargetY;
bool bNewPath = false;
// Only do anything if the actor needs to move!
if (pMover->targetX == -1 && pMover->targetY == -1)
return;
if (pMover->bStop) {
GotThere(pMover);
pMover->bStop = false;
pMover->walkNumber++;
SetMoverStanding(pMover);
return;
}
#if SLOW_RINCE_DOWN
/**/ if (BogusVar++ < Interlude) // Temporary slow-down-the-action code
/**/ return; //
/**/ BogusVar = 0; //
#endif
if (!TinselV2) {
// During swalk()s, movement while hidden may be slowed down.
if (pMover->bHidden) {
if (++hSlowVar < pMover->SlowFactor)
return;
hSlowVar = 0;
}
}
// 'push' the target
sTargetX = pMover->targetX;
sTargetY = pMover->targetY;
NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY,
&newx, &newy, &status, &s2, &hS2p, pMover->over, false, pMover);
if (newx == pMover->objX && newy == pMover->objY) {
// 'pop' the target
pMover->targetX = sTargetX;
pMover->targetY = sTargetY;
NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY, &newx, &newy,
&status, &s2, &hS2p, pMover->over, true, pMover);
if (newx == pMover->objX && newy == pMover->objY) {
NotMoving(pMover, newx, newy);
return;
}
}
// Find out which path we're in now
hPath = InPolygon(newx, newy, PATH);
if (hPath == NOPOLY) {
if (pMover->bNoPath) {
Move(pMover, newx, newy, pMover->hCpath);
return;
} else {
// May be marginally outside!
// OR bIgPath may be set.
hPath = pMover->hCpath;
}
} else if (pMover->bNoPath) {
pMover->bNoPath = false;
bNewPath = true;
} else if (hPath != pMover->hCpath) {
if (IsInPolygon(newx, newy, pMover->hCpath))
hPath = pMover->hCpath;
}
CheckScale(pMover, hPath, newy);
/*
* Must stay out of moving actors' blocking polygons.
*/
ma = InMoverBlock(pMover, newx, newy);
if (ma != NULL) {
// Stop if there's no chance of arriving
if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
GotThere(pMover);
return;
}
if (InMoverBlock(pMover, pMover->objX, pMover->objY))
;
else {
hEb = InitExtraBlock(pMover, ma);
newx = pMover->objX;
newy = pMover->objY;
BlockingCorner(hEb, &newx, &newy, pMover->ItargetX, pMover->ItargetY);
SetMoverDest(pMover, newx, newy);
return;
}
}
/*--------------------------------------
This is where it actually gets moved.
--------------------------------------*/
Move(pMover, newx, newy, hPath);
// Entering a new path polygon?
if (hPath != pMover->hCpath || bNewPath)
EnteringNewPath(pMover, hPath, newx, newy);
}
/**
* Store the default refer type for the current scene.
*/
void SetDefaultRefer(int32 defRefer) {
DefaultRefer = defRefer;
}
int GetLastLeadXdest() {
return lastLeadXdest;
}
int GetLastLeadYdest() {
return lastLeadYdest;
}
/**
* DoMoveActor
*/
void DoMoveActor(PMOVER pActor) {
int wasx, wasy;
int i;
#define NUMBER 1
wasx = pActor->objX;
wasy = pActor->objY;
MoveActor(pActor);
if ((pActor->targetX != -1 || pActor->targetY != -1)
&& (wasx == pActor->objX && wasy == pActor->objY)) {
for (i=0; i < NUMBER; i++) {
MoveActor(pActor);
if (wasx != pActor->objX || wasy != pActor->objY)
break;
}
// assert(i<NUMBER);
}
}
} // End of namespace Tinsel