/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-1-1/engines/tinsel/move.cpp $
 * $Id: move.cpp 45616 2009-11-02 21:54:57Z fingolfin $
 *
 * 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 --------------------

#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 neighbouring 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-centre 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 centre. */
				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 = PolyCentreX(hIpath);
					pMover->ItargetY = PolyCentreY(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 = PolyCentreX(hPath);
					y = PolyCentreY(hPath);
					SetMoverDest(pMover, x, y);
					pMover->InDifficulty = TRY_CENTRE;
					pMover->over = false;
				} else if (pMover->InDifficulty == TRY_CENTRE) {
					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-centre.  |
				 ----------------------------------------------------------*/
				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, PolyCentreX(hPath), PolyCentreY(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 = PolyCentreX(hIpath);
				y = PolyCentreY(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, PolyCentreX(hIpath), PolyCentreY(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, PolyCentreX(hIpath), PolyCentreY(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
