1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * Handles walking and use of the path system.
22  *
23  * Contains the dodgiest code in the whole system.
24  */
25 
26 #include "tinsel/actors.h"
27 #include "tinsel/anim.h"
28 #include "tinsel/background.h"
29 #include "tinsel/cursor.h"
30 #include "tinsel/dw.h"
31 #include "tinsel/graphics.h"
32 #include "tinsel/move.h"
33 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
34 #include "tinsel/object.h"
35 #include "tinsel/polygons.h"
36 #include "tinsel/movers.h"
37 #include "tinsel/scroll.h"
38 #include "tinsel/tinlib.h"	// For Stand()
39 #include "tinsel/tinsel.h"
40 
41 namespace Tinsel {
42 
43 //----------------- DEVELOPMENT OPTIONS --------------------
44 
45 #define SLOW_RINCE_DOWN		0
46 
47 //----------------- EXTERNAL FUNCTIONS ---------------------
48 
49 // in POLYGONS.C
50 // Deliberatley defined here, and not in polygons.h
51 HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta);
52 
53 //----------------- LOCAL DEFINES --------------------
54 
55 #define XMDIST	(TinselV2 ? 6 : 4)
56 #define XHMDIST	(TinselV2 ? 3 : 2)
57 #define YMDIST	(TinselV2 ? 3 : 2)
58 #define YHMDIST	(TinselV2 ? 3 : 2)
59 
60 #define XTHERE		1
61 #define XRESTRICT	2
62 #define YTHERE		4
63 #define YRESTRICT	8
64 #define STUCK		16
65 
66 #define LEAVING_PATH	0x100
67 #define ENTERING_BLOCK	0x200
68 #define ENTERING_MBLOCK	0x400
69 
70 #define ALL_SORTED	1
71 #define NOT_SORTED	0
72 
73 #define STEPS_MAX (TinselV2 ? 12 : 6)
74 
75 //----------------- LOCAL GLOBAL DATA --------------------
76 
77 // These vars are reset upon engine destruction
78 
79 #if SLOW_RINCE_DOWN
80 static int g_Interlude = 0;	// For slowing down walking, for testing
81 static int g_BogusVar = 0;	// For slowing down walking, for testing
82 #endif
83 
84 static int32 g_DefaultRefer = 0;
85 static int g_lastLeadXdest = 0, g_lastLeadYdest = 0;
86 
87 static int g_hSlowVar = 0;	// used by MoveActor()
88 
89 
90 //----------------- FORWARD REFERENCES --------------------
91 
92 static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
93 			int *newx, int *newy, int *s1, int *s2, HPOLYGON *hS2p,
94 			bool bOver, bool bBodge,
95 			PMOVER pActor, PMOVER *collisionActor = 0);
96 
97 
98 #if SLOW_RINCE_DOWN
99 /**
100  * AddInterlude
101  */
102 
AddInterlude(int n)103 void AddInterlude(int n) {
104 	g_Interlude += n;
105 	if (g_Interlude < 0)
106 		g_Interlude = 0;
107 }
108 #endif
109 
ResetVarsMove()110 void ResetVarsMove() {
111 	g_DefaultRefer = 0;
112 	g_lastLeadXdest = g_lastLeadYdest = 0;
113 	g_hSlowVar = 0;
114 }
115 
116 /**
117  * Given (x, y) of a click within a path polygon, checks that the
118  * co-ordinates are not within a blocking polygon. If it is not, the
119  * destination is the click point, otherwise tries to find a legal point
120  * below or above the click point.
121  * Returns:
122  *	NOT_SORTED - if a destination is worked out (movement required)
123  *	ALL_SORTED - no destination found (so no movement required)
124  */
ClickedOnPath(int clickX,int clickY,int * ptgtX,int * ptgtY)125 static int ClickedOnPath(int clickX, int clickY, int *ptgtX, int *ptgtY) {
126 	int Loffset, Toffset;
127 	int i;
128 
129 	/*--------------------------------------
130 	 Clicked within a path,
131 	 go to where requested unless blocked.
132 	 --------------------------------------*/
133 	if (InPolygon(clickX, clickY, BLOCK) == NOPOLY) {
134 		// Not in a blocking polygon - go to where requested.
135 		*ptgtX = clickX;
136 		*ptgtY = clickY;
137 	} else {
138 		/*------------------------------------------------------
139 		 In a Blocking polygon - try searching down and up.
140 		 If still nowhere (for now) give up!
141 		 ------------------------------------------------------*/
142 		_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
143 
144 		for (i = clickY+1; i < SCREEN_HEIGHT + Toffset; i++) {
145 			// Don't leave the path system
146 			if (InPolygon(clickX, i, PATH) == NOPOLY) {
147 				i = SCREEN_HEIGHT;
148 				break;
149 			}
150 			if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
151 				*ptgtX = clickX;
152 				*ptgtY = i;
153 				break;
154 			}
155 		}
156 		if (i == SCREEN_HEIGHT) {
157 			for (i = clickY-1; i >= Toffset; i--) {
158 				// Don't leave the path system
159 				if (InPolygon(clickX, i, PATH) == NOPOLY) {
160 					i = -1;
161 					break;
162 				}
163 				if (InPolygon(clickX, i, BLOCK) == NOPOLY) {
164 					*ptgtX = clickX;
165 					*ptgtY = i;
166 					break;
167 				}
168 			}
169 		}
170 		if (i < 0) {
171 			return ALL_SORTED;
172 		}
173 	}
174 	return NOT_SORTED;
175 }
176 
177 /**
178  * Given (x, y) of a click within a referral polygon, works out the
179  * destination according to the referral type.
180  * Returns:
181  *   NOT_SORTED - if a destination is worked out (movement required)
182  *   ALL_SORTED - no destination found (so no movement required)
183  */
ClickedOnRefer(HPOLYGON hRefpoly,int clickX,int clickY,int * ptgtX,int * ptgtY)184 static int ClickedOnRefer(HPOLYGON hRefpoly, int clickX, int clickY, int *ptgtX, int *ptgtY) {
185 	int	i;
186 	int	end;		// Extreme of the scene
187 	int	Loffset, Toffset;
188 
189 	_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
190 	*ptgtX = *ptgtY = -1;
191 
192 	switch (PolySubtype(hRefpoly)) {
193 	case REF_POINT:				// Go to specified node
194 		GetPolyNode(hRefpoly, ptgtX, ptgtY);
195 		assert(InPolygon(*ptgtX, *ptgtY, PATH) != NOPOLY); // POINT Referral to illegal point
196 		break;
197 
198 	case REF_DOWN:				// Search downwards
199 		end = _vm->_bg->BgHeight();
200 		for (i = clickY+1; i < end; i++)
201 			if (InPolygon(clickX, i, PATH) != NOPOLY
202 					&& InPolygon(clickX, i, BLOCK) == NOPOLY) {
203 				*ptgtX = clickX;
204 				*ptgtY = i;
205 				break;
206 			}
207 		break;
208 
209 	case REF_UP:				// Search upwards
210 		for (i = clickY-1; i >= 0; i--)
211 			if (InPolygon(clickX, i, PATH) != NOPOLY
212 					&& InPolygon(clickX, i, BLOCK) == NOPOLY) {
213 				*ptgtX = clickX;
214 				*ptgtY = i;
215 				break;
216 			}
217 		break;
218 
219 	case REF_RIGHT:				// Search to the right
220 		end = _vm->_bg->BgWidth();
221 		for (i = clickX+1; i < end; i++)
222 			if (InPolygon(i, clickY, PATH) != NOPOLY
223 			&& InPolygon(i, clickY, BLOCK) == NOPOLY) {
224 				*ptgtX = i;
225 				*ptgtY = clickY;
226 				break;
227 			}
228 		break;
229 
230 	case REF_LEFT:				// Search to the left
231 		for (i = clickX-1; i >= 0; i--)
232 			if (InPolygon(i, clickY, PATH) != NOPOLY
233 			&& InPolygon(i, clickY, BLOCK) == NOPOLY) {
234 				*ptgtX = i;
235 				*ptgtY = clickY;
236 				break;
237 			}
238 		break;
239 
240 	default:
241 		break;
242 	}
243 	if (*ptgtX != -1 && *ptgtY != -1) {
244 		return NOT_SORTED;
245 	} else
246 		return ALL_SORTED;
247 }
248 
249 /**
250  * Given (x, y) of a click, works out the destination according to the
251  * default referral type.
252  * Returns:
253  *   NOT_SORTED - if a destination is worked out (movement required)
254  *   ALL_SORTED - no destination found (so no movement required)
255  */
ClickedOnNothing(int clickX,int clickY,int * ptgtX,int * ptgtY)256 static int ClickedOnNothing(int clickX, int clickY, int *ptgtX, int *ptgtY) {
257 	int	i;
258 	int	end;		// Extreme of the scene
259 	int	Loffset, Toffset;
260 
261 	_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
262 
263 	switch (g_DefaultRefer) {
264 	case REF_DEFAULT:
265 		// Try searching down and up (onscreen).
266 		for (i = clickY+1; i < SCREEN_HEIGHT+Toffset; i++)
267 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
268 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
269 			}
270 		for (i = clickY-1; i >= Toffset; i--)
271 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
272 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
273 			}
274 		// Try searching down and up (offscreen).
275 		end = _vm->_bg->BgHeight();
276 		for (i = clickY+1; i < end; i++)
277 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
278 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
279 			}
280 		for (i = clickY-1; i >= 0; i--)
281 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
282 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
283 			}
284 		break;
285 
286 	case REF_UP:
287 		for (i = clickY-1; i >= 0; i--)
288 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
289 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
290 			}
291 		break;
292 
293 	case REF_DOWN:
294 		end = _vm->_bg->BgHeight();
295 		for (i = clickY+1; i < end; i++)
296 			if (InPolygon(clickX, i, PATH) != NOPOLY) {
297 				return ClickedOnPath(clickX, i, ptgtX, ptgtY);
298 			}
299 		break;
300 
301 	case REF_LEFT:
302 		for (i = clickX-1; i >= 0; i--)
303 			if (InPolygon(i, clickY, PATH) != NOPOLY) {
304 				return ClickedOnPath(i, clickY, ptgtX, ptgtY);
305 			}
306 		break;
307 
308 	case REF_RIGHT:
309 		end = _vm->_bg->BgWidth();
310 		for (i = clickX + 1; i < end; i++)
311 			if (InPolygon(i, clickY, PATH) != NOPOLY) {
312 				return ClickedOnPath(i, clickY, ptgtX, ptgtY);
313 			}
314 		break;
315 
316 	default:
317 		break;
318 	}
319 
320 	// Going nowhere!
321 	return ALL_SORTED;
322 }
323 
324 /**
325  * Given (x, y) of the click, ascertains whether the click is within a
326  * path, within a referral poly, or niether. The appropriate function
327  * then gets called to give us a revised destination.
328  * Returns:
329  *   NOT_SORTED - if a destination is worked out (movement required)
330  *   ALL_SORTED - no destination found (so no movement required)
331  */
WorkOutDestination(int clickX,int clickY,int * ptgtX,int * ptgtY)332 static int WorkOutDestination(int clickX, int clickY, int *ptgtX, int *ptgtY) {
333 	HPOLYGON hPoly;
334 
335 	/*--------------------------------------
336 	 Clicked within a path?
337 	 if not, within a referral poly?
338 	 if not, try and sort something out.
339 	---------------------------------------*/
340 	if (InPolygon(clickX, clickY, PATH) != NOPOLY) {
341 		return ClickedOnPath(clickX, clickY, ptgtX, ptgtY);
342 	} else if ((hPoly = InPolygon(clickX, clickY, REFER)) != NOPOLY) {
343 		return ClickedOnRefer(hPoly, clickX, clickY, ptgtX, ptgtY);
344 	} else {
345 		return ClickedOnNothing(clickX, clickY, ptgtX, ptgtY);
346 	}
347 }
348 
349 /**
350  * Work out which reel to adopt for a section of movement.
351  */
GetDirection(int fromx,int fromy,int tox,int toy,DIRECTION lastreel,HPOLYGON hPath,YBIAS yBias)352 DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
353 							  HPOLYGON hPath, YBIAS yBias) {
354 	int	xchange = 0, ychange = 0;
355 	enum {X_NONE, X_LEFT, X_RIGHT, X_NO} xdir;
356 	enum {Y_NONE, Y_UP, Y_DOWN, Y_NO} ydir;
357 
358 	DIRECTION	reel = lastreel;	// Leave alone if can't decide
359 
360 	/*
361 	 * Determine size and direction of X movement.
362 	 * i.e. left, right, none or not allowed.
363 	 */
364 	if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_VERT))
365 		xdir = X_NO;
366 	else if (tox == -1)
367 		xdir = X_NONE;
368 	else {
369 		xchange = tox - fromx;
370 		if (xchange > 0)
371 			xdir = X_RIGHT;
372 		else if (xchange < 0) {
373 			xchange = -xchange;
374 			xdir = X_LEFT;
375 		} else
376 			xdir = X_NONE;
377 	}
378 
379 	/*
380 	 * Determine size and direction of Y movement.
381 	 * i.e. up, down, none or not allowed.
382 	 */
383 	if (hPath != NOPOLY && (GetPolyReelType(hPath) == REEL_HORIZ))
384 		ydir = Y_NO;
385 	else if (toy == -1)
386 		ydir = Y_NONE;
387 	else {
388 		ychange = toy - fromy;
389 		if (ychange > 0)
390 			ydir = Y_DOWN;
391 		else if (ychange < 0) {
392 			ychange = -ychange;
393 			ydir = Y_UP;
394 		} else
395 			ydir = Y_NONE;
396 	}
397 
398 	/*
399 	 * Some adjustment to allow for different x and y pixell sizes.
400 	 */
401 	switch (yBias) {
402 	case YB_X2:
403 		ychange += ychange;		// Double y distance to cover
404 		break;
405 
406 	case YB_X1_5:
407 		ychange += ychange / 2;	// Double y distance to cover
408 		break;
409 
410 	default:
411 		break;
412 	}
413 
414 	/*
415 	 * Determine which reel to use.
416 	 */
417 	if (xdir == X_NO) {
418 		// Forced to be FORWARD or AWAY
419 		switch (ydir) {
420 		case Y_DOWN:
421 			reel = FORWARD;
422 			break;
423 		case Y_UP:
424 			reel = AWAY;
425 			break;
426 		default:
427 			if (reel != AWAY)	// No gratuitous turn
428 				reel = FORWARD;
429 			break;
430 		}
431 	} else if (ydir == Y_NO) {
432 		// Forced to be LEFTREEL or RIGHTREEL
433 		switch (xdir) {
434 		case X_LEFT:
435 			reel = LEFTREEL;
436 			break;
437 		case X_RIGHT:
438 			reel = RIGHTREEL;
439 			break;
440 		default:
441 			if (reel != LEFTREEL)	// No gratuitous turn
442 				reel = RIGHTREEL;
443 			break;
444 		}
445 	} else if (xdir != X_NONE || ydir != Y_NONE) {
446 		if (xdir == X_NONE)
447 			reel = (ydir == Y_DOWN) ? FORWARD : AWAY;
448 		else if (ydir == Y_NONE)
449 			reel = (xdir == X_LEFT) ? LEFTREEL : RIGHTREEL;
450 		else {
451 			bool DontBother = false;
452 
453 			if (xchange <= 4 && ychange <= 4) {
454 				switch (reel) {
455 				case LEFTREEL:
456 					if (xdir == X_LEFT)
457 						DontBother = true;
458 					break;
459 				case RIGHTREEL:
460 					if (xdir == X_RIGHT)
461 						DontBother = true;
462 					break;
463 				case FORWARD:
464 					if (ydir == Y_DOWN)
465 						DontBother = true;
466 					break;
467 				case AWAY:
468 					if (ydir == Y_UP)
469 						DontBother = true;
470 					break;
471 				default:
472 					break;
473 				}
474 			}
475 			if (!DontBother) {
476 				if (xchange > ychange)
477 					reel = (xdir == X_LEFT) ? LEFTREEL : RIGHTREEL;
478 				else
479 					reel = (ydir == Y_DOWN) ? FORWARD : AWAY;
480 			}
481 		}
482 	}
483 	return reel;
484 }
485 
486 /**
487  * Haven't moved, look towards the cursor.
488  */
GotThereWithoutMoving(PMOVER pActor)489 static void GotThereWithoutMoving(PMOVER pActor) {
490 	int	curX, curY;
491 	DIRECTION	reel;
492 
493 	if (!pActor->bSpecReel) {
494 		_vm->_cursor->GetCursorXYNoWait(&curX, &curY, true);
495 
496 		reel = GetDirection(pActor->objX, pActor->objY, curX, curY, pActor->direction, pActor->hCpath);
497 
498 		if (reel != pActor->direction)
499 			SetMoverWalkReel(pActor, reel, pActor->scale, false);
500 	}
501 }
502 
503 /**
504  * Arrived at final destination.
505  */
GotThere(PMOVER pMover)506 static void GotThere(PMOVER pMover) {
507 	pMover->targetX = pMover->targetY = -1;		// 4/1/95
508 	pMover->ItargetX = pMover->ItargetY = -1;
509 	pMover->UtargetX = pMover->UtargetY = -1;
510 
511 	// Perhaps we have'nt moved.
512 	if (pMover->objX == (int)pMover->walkedFromX && pMover->objY == (int)pMover->walkedFromY) {
513 		// Got there without moving
514 		if (!TinselV2)
515 			GotThereWithoutMoving(pMover);
516 		else if (!pMover->bSpecReel) {
517 			// No tag reel, look at cursor
518 			int curX, curY;
519 			DIRECTION direction;
520 
521 			_vm->_cursor->GetCursorXY(&curX, &curY, true);
522 			direction = GetDirection(pMover->objX, pMover->objY,
523 						curX, curY,
524 						pMover->direction,
525 						pMover->hCpath,
526 						YB_X2);
527 
528 			if (direction != pMover->direction)
529 				SetMoverWalkReel(pMover, direction, pMover->scale, false);
530 		}
531 	}
532 
533 	if (!TinselV2)
534 		_vm->_actor->ReTagActor(pMover->actorID);	// Tag allowed while stationary
535 
536 	SetMoverStanding(pMover);
537 	pMover->bMoving = false;
538 
539 	if (TinselV2 && pMover->bIgPath && pMover->zOverride != -1
540 			&&  InPolygon(pMover->objX, pMover->objY, PATH) == NOPOLY)
541 		// New feature for end-of-scene walk-outs
542 		SetMoverZ(pMover, pMover->objY, pMover->zOverride);
543 }
544 
545 enum cgt { GT_NOTL, GT_NOTB, GT_NOT2, GT_OK, GT_MAY };
546 
547 /**
548  * Can we get straight there?
549  */
CanGetThere(PMOVER pActor,int tx,int ty)550 static cgt CanGetThere(PMOVER pActor, int tx, int ty) {
551 	int s1, s2;		// s2 not used here!
552 	HPOLYGON hS2p;		// nor is s2p!
553 	int nextx, nexty;
554 
555 	int targetX = tx;
556 	int targetY = ty;		// Ultimate destination
557 	int x = pActor->objX;
558 	int y = pActor->objY;		// Present position
559 
560 	while (targetX != -1 || targetY != -1) {
561 		NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
562 				&s1, &s2, &hS2p, pActor->over, false, pActor);
563 
564 		if (s1 == (XTHERE | YTHERE)) {
565 			return GT_OK;	// Can get there directly.
566 		} else if (s1 == (XTHERE | YRESTRICT) || s1 == (YTHERE | XRESTRICT)) {
567 			return GT_MAY;	// Can't get there directly.
568 		} else if (s1 & STUCK) {
569 			if (s2 == LEAVING_PATH)
570 				return GT_NOTL;	// Can't get there.
571 			else
572 				return GT_NOTB;	// Can't get there.
573 		} else if (x == nextx && y == nexty) {
574 			return GT_NOT2;	// Can't get there.
575 		}
576 		x = nextx;
577 		y = nexty;
578 	}
579 	return GT_MAY;
580 }
581 
582 
583 /**
584  * Set final destination.
585  */
SetMoverUltDest(PMOVER pActor,int x,int y)586 static void SetMoverUltDest(PMOVER pActor, int x, int y) {
587 	pActor->UtargetX = x;
588 	pActor->UtargetY = y;
589 	pActor->hUpath = InPolygon(x, y, PATH);
590 
591 	assert(pActor->hUpath != NOPOLY || pActor->bIgPath); // Invalid ultimate destination
592 }
593 
594 /**
595  * Set intermediate destination.
596  *
597  * If in final destination path, go straight to target.
598  * If in a neighboring path to the final destination, if the target path
599  * is a follow nodes path, head for the end node, otherwise head straight
600  * for the target.
601  * Otherwise, head towards the pseudo-center or end node of the first
602  * en-route path.
603  */
SetMoverIntDest(PMOVER pMover,int x,int y)604 static void SetMoverIntDest(PMOVER pMover, int x, int y) {
605 	HPOLYGON hIpath, hTpath;
606 	int	node;
607 
608 	hTpath = InPolygon(x, y, PATH);		// Target path
609 #ifdef DEBUG
610 	if (!pMover->bIgPath)
611 		assert(hTpath != NOPOLY); // SetMoverIntDest() - target not in path
612 #endif
613 
614 	if (pMover->hCpath == hTpath || pMover->bIgPath
615 		|| IsInPolygon(pMover->objX, pMover->objY, hTpath)) {
616 		// In destination path - head straight for the target.
617 		pMover->ItargetX = x;
618 		pMover->ItargetY = y;
619 		// make damn sure that Itarget is in hIpath
620 		pMover->hIpath = !TinselV2 ? hTpath : InPolygon(x, y, PATH);
621 	} else if (IsAdjacentPath(pMover->hCpath, hTpath)) {
622 		// In path adjacent to target
623 		if (PolySubtype(hTpath) != NODE) {
624 			// Target path is normal - head for target.
625 			// Added 26/01/95, innroom
626 			if (CanGetThere(pMover, x, y) == GT_NOTL) {
627 				NearestCorner(&x, &y, pMover->hCpath, hTpath);
628 			}
629 			pMover->ItargetX = x;
630 			pMover->ItargetY = y;
631 			if (TinselV2)
632 				// make damn sure that Itarget is in hIpath
633 				pMover->hIpath = InPolygon(x, y, PATH);
634 		} else {
635 			// Target path is node - head for end node.
636 			node = NearestEndNode(hTpath, pMover->objX, pMover->objY);
637 			getNpathNode(hTpath, node, &pMover->ItargetX, &pMover->ItargetY);
638 			if (TinselV2)
639 				// make damn sure that Itarget is in hIpath
640 				pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
641 		}
642 		if (!TinselV2)
643 			pMover->hIpath = hTpath;
644 	} else {
645 		assert(hTpath != NOPOLY); // Error 701
646 		hIpath = GetPathOnTheWay(pMover->hCpath, hTpath);
647 
648 		if (TinselV2 && (hIpath == NOPOLY)) {
649 			pMover->hIpath = NOPOLY;
650 		} else if (hIpath != NOPOLY) {
651 			/* Head for an en-route path */
652 			if (PolySubtype(hIpath) != NODE) {
653 				/* En-route path is normal - head for pseudo center. */
654 				if (CanGetThere(pMover, x, y) == GT_OK) {
655 					pMover->ItargetX = x;
656 					pMover->ItargetY = y;
657 					if (TinselV2)
658 						// make damn sure that Itarget is in hIpath
659 						pMover->hIpath = InPolygon(x, y, PATH);
660 				} else {
661 					pMover->ItargetX = PolyCenterX(hIpath);
662 					pMover->ItargetY = PolyCenterY(hIpath);
663 					if (TinselV2)
664 						// make damn sure that Itarget is in hIpath
665 						pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
666 				}
667 			} else {
668 				/* En-route path is node - head for end node. */
669 				node = NearestEndNode(hIpath, pMover->objX, pMover->objY);
670 				getNpathNode(hIpath, node, &pMover->ItargetX, &pMover->ItargetY);
671 				if (TinselV2)
672 					// make damn sure that Itarget is in hIpath
673 					pMover->hIpath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
674 			}
675 			if (!TinselV2)
676 				pMover->hIpath = hIpath;
677 		}
678 	}
679 
680 	pMover->InDifficulty = NO_PROB;
681 }
682 
683 /**
684  * Set short-term destination and adopt the appropriate reel.
685  */
SetMoverDest(PMOVER pActor,int x,int y)686 static void SetMoverDest(PMOVER pActor, int x, int y) {
687 	int	scale;
688 	DIRECTION	reel;
689 
690 	// Set the co-ordinates requested.
691 	pActor->targetX = x;
692 	pActor->targetY = y;
693 	pActor->InDifficulty = NO_PROB;
694 
695 	reel = GetDirection(pActor->objX, pActor->objY, x, y, pActor->direction, pActor->hCpath);
696 	scale = GetScale(pActor->hCpath, pActor->objY);
697 	if (scale != pActor->scale || reel != pActor->direction) {
698 		SetMoverWalkReel(pActor, reel, scale, false);
699 	}
700 }
701 
702 /**
703  * SetNextDest
704  */
SetNextDest(PMOVER pMover)705 static void SetNextDest(PMOVER pMover) {
706 	int	targetX, targetY;		// Ultimate destination
707 	int	x, y;				// Present position
708 	int	nextx, nexty;
709 	int	s1, lstatus = 0;
710 	int	s2;
711 	HPOLYGON	hS2p;
712 	int	i;
713 	HPOLYGON	hNpoly;
714 	HPOLYGON	hPath;
715 	int	znode;
716 	int	nx, ny;
717 	int	sx, sy;
718 	HPOLYGON	hEb;
719 
720 	int	ss1, ss2;
721 	HPOLYGON shS2p;
722 	PMOVER collisionActor;
723 #if 1
724 	int	sTargetX, sTargetY;
725 #endif
726 
727 	/*
728 	 * Desired destination (Itarget) is already set
729 	 */
730 	x = pMover->objX;		// Current position
731 	y = pMover->objY;
732 	targetX = pMover->ItargetX;	// Desired position
733 	targetY = pMover->ItargetY;
734 
735 	/*
736 	 * If we're where we're headed, end it all (the moving).
737 	 */
738 //	if (x == targetX && y == targetY)
739 	if (ABS(x - targetX) < XMDIST && ABS(y - targetY) < YMDIST) {
740 		if (targetX == pMover->UtargetX && targetY == pMover->UtargetY) {
741 			// Desired position
742 			GotThere(pMover);
743 			return;
744 		} else {
745 			assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5001
746 			SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
747 		}
748 	}
749 
750 	if (pMover->bNoPath || pMover->bIgPath) {
751 		/* Can get there directly. */
752 		SetMoverDest(pMover, targetX, targetY);
753 		pMover->over = false;
754 		return;
755 	}
756 
757 	/*----------------------------------------------------------------------
758 	| Some work to do here if we're in a follow nodes polygon - basically
759 	| head for the next node.
760 	----------------------------------------------------------------------*/
761 	hNpoly = pMover->hFnpath;		// The node path we're in (if any)
762 	switch (pMover->npstatus) {
763 	case NOT_IN:
764 	default:
765 		break;
766 
767 	case ENTERING:
768 		znode = NearestEndNode(hNpoly, x, y);
769 		/* Hang on, we're probably here already! */
770 		if (znode) {
771 			pMover->npstatus = GOING_DOWN;
772 			pMover->line = znode-1;
773 			getNpathNode(hNpoly, znode - 1, &nx, &ny);
774 		} else {
775 			pMover->npstatus = GOING_UP;
776 			pMover->line = znode;
777 			getNpathNode(hNpoly, 1, &nx, &ny);
778 		}
779 		SetMoverDest(pMover, nx, ny);
780 
781 		// Test for pseudo-one-node npaths
782 		if (numNodes(hNpoly) == 2 &&
783 				ABS(pMover->objX - pMover->targetX) < XMDIST &&
784 				ABS(pMover->objY - pMover->targetY) < YMDIST) {
785 			// That's enough, we're leaving
786 			pMover->npstatus = LEAVING;
787 		} else {
788 			// Normal situation
789 			pMover->over = true;
790 			return;
791 		}
792 		// fall through
793 
794 	case LEAVING:
795 		assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5002
796 		SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
797 		targetX = pMover->ItargetX;	// Desired position
798 		targetY = pMover->ItargetY;
799 		break;
800 
801 	case GOING_UP:
802 		i = pMover->line;		// The line we're on
803 
804 		// Is this the final target line?
805 		if (i+1 == pMover->Tline && hNpoly == pMover->hUpath) {
806 			// The final leg of the journey
807 			pMover->line = i+1;
808 			SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
809 			pMover->over = false;
810 			return;
811 		} else {
812 			// Go to the next node unless we're at the last one
813 			i++;				// The node we're at
814 			if (++i < numNodes(hNpoly)) {
815 				getNpathNode(hNpoly, i, &nx, &ny);
816 				SetMoverDest(pMover, nx, ny);
817 				pMover->line = i-1;
818 				if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
819 				   ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
820 					pMover->over = false;
821 				else
822 					pMover->over = true;
823 				return;
824 			} else {
825 				// Last node - we're off
826 				pMover->npstatus = LEAVING;
827 				assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5003
828 				SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
829 				targetX = pMover->ItargetX;	// Desired position
830 				targetY = pMover->ItargetY;
831 				break;
832 			}
833 		}
834 
835 	case GOING_DOWN:
836 		i = pMover->line;		// The line we're on and the node we're at
837 
838 		// Is this the final target line?
839 		if (i - 1 == pMover->Tline && hNpoly == pMover->hUpath) {
840 			// The final leg of the journey
841 			SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
842 			pMover->line = i-1;
843 			pMover->over = false;
844 			return;
845 		} else {
846 			// Go to the next node unless we're at the last one
847 			if (--i >= 0) {
848 				getNpathNode(hNpoly, i, &nx, &ny);
849 				SetMoverDest(pMover, nx, ny);
850 				pMover->line--;		/* The next node to head for */
851 				if (ABS(pMover->UtargetX - pMover->targetX) < XMDIST &&
852 				   ABS(pMover->UtargetY - pMover->targetY) < YMDIST)
853 					pMover->over = false;
854 				else
855 					pMover->over = true;
856 				return;
857 			} else {
858 				// Last node - we're off
859 				pMover->npstatus = LEAVING;
860 				assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5004
861 				SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
862 				targetX = pMover->ItargetX;	// Desired position
863 				targetY = pMover->ItargetY;
864 				break;
865 			}
866 		}
867 	}
868 
869 
870 
871 
872 	/*------------------------------------------------------
873 	| See if it can get there directly. There may be an
874 	| intermediate destination to head for.
875 	------------------------------------------------------*/
876 
877 	while (targetX != -1 || targetY != -1) {
878 #if 1
879 		// 'push' the target
880 		sTargetX = targetX;
881 		sTargetY = targetY;
882 #endif
883 		NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
884 					&s1, &s2, &hS2p, pMover->over, false, pMover, &collisionActor);
885 
886 		if (s1 != (XTHERE | YTHERE) && x == nextx && y == nexty) {
887 			ss1 = s1;
888 			ss2 = s2;
889 			shS2p = hS2p;
890 #if 1
891 			// 'pop' the target
892 			targetX = sTargetX;
893 			targetY = sTargetY;
894 #endif
895 			// Note: this aint right - targetX/Y (may) have been
896 			// nobbled by that last call to NewCoOrdinates()
897 			// Re-instating them (can) leads to oscillation
898 			NewCoOrdinates(x, y, &targetX, &targetY, &nextx, &nexty,
899 						&s1, &s2, &hS2p, pMover->over, true, pMover, &collisionActor);
900 
901 			if (x == nextx && y == nexty) {
902 				s1 = ss1;
903 				s2 = ss2;
904 				hS2p = shS2p;
905 			}
906 		}
907 
908 		if (s1 == (XTHERE | YTHERE)) {
909 			/* Can get there directly. */
910 			SetMoverDest(pMover, nextx, nexty);
911 			pMover->over = false;
912 			break;
913 		} else if ((s1 & STUCK) || s1 == (XRESTRICT + YRESTRICT)
914 		     || s1 == (XTHERE | YRESTRICT) || s1 == (YTHERE | XRESTRICT)) {
915 			/*-------------------------------------------------
916 			 Can't go any further in this direction.	   |
917 			 If it's because of a blocking polygon, try to do |
918 			 something about it.				   |
919 			 -------------------------------------------------*/
920 			if (s2 & ENTERING_BLOCK) {
921 				x = pMover->objX;	// Current position
922 				y = pMover->objY;
923 				// Go to the nearest corner of the blocking polygon concerned
924 				BlockingCorner(hS2p, &x, &y, pMover->ItargetX, pMover->ItargetY);
925 				SetMoverDest(pMover, x, y);
926 				pMover->over = false;
927 			} else if (s2 & ENTERING_MBLOCK) {
928 				if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
929 					// The best we're going to achieve
930 					SetMoverUltDest(pMover, x, y);
931 					SetMoverDest(pMover, x, y);
932 				} else {
933 					sx = pMover->objX;
934 					sy = pMover->objY;
935 //					pMover->objX = x;
936 //					pMover->objY = y;
937 
938 					hEb = InitExtraBlock(pMover, collisionActor);
939 					x = pMover->objX;
940 					y = pMover->objY;
941 					BlockingCorner(hEb, &x, &y, pMover->ItargetX, pMover->ItargetY);
942 
943 					pMover->objX = sx;
944 					pMover->objY = sy;
945 					SetMoverDest(pMover, x, y);
946 					pMover->over = false;
947 				}
948 			} else {
949 				/*----------------------------------------
950 				 Currently, this is as far as we can go. |
951 				 Definitely room for improvement here!   |
952 				 ----------------------------------------*/
953 				hPath = InPolygon(pMover->ItargetX, pMover->ItargetY, PATH);
954 				if (hPath != pMover->hIpath) {
955 					if (IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hIpath))
956 						hPath = pMover->hIpath;
957 				}
958 				assert(hPath == pMover->hIpath);
959 
960 				if (pMover->InDifficulty == NO_PROB) {
961 					x = PolyCenterX(hPath);
962 					y = PolyCenterY(hPath);
963 					SetMoverDest(pMover, x, y);
964 					pMover->InDifficulty = TRY_CENTER;
965 					pMover->over = false;
966 				} else if (pMover->InDifficulty == TRY_CENTER) {
967 					NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
968 					SetMoverDest(pMover, x, y);
969 					pMover->InDifficulty = TRY_CORNER;
970 					pMover->over = false;
971 				} else if (pMover->InDifficulty == TRY_CORNER) {
972 					NearestCorner(&x, &y, pMover->hCpath, pMover->hIpath);
973 					SetMoverDest(pMover, x, y);
974 					pMover->InDifficulty = TRY_NEXTCORNER;
975 					pMover->over = false;
976 				}
977 			}
978 			break;
979 		}
980 		else if (((lstatus & YRESTRICT) && !(s1 & YRESTRICT))
981 		     ||  ((lstatus & XRESTRICT) && !(s1 & XRESTRICT))) {
982 			/*-----------------------------------------------
983 			 A restriction in a direction has been removed. |
984 			 Use this as an intermediate destination.	 |
985 			 -----------------------------------------------*/
986 			SetMoverDest(pMover, nextx, nexty);
987 			pMover->over = false;
988 			break;
989 		}
990 
991 		x = nextx;
992 		y = nexty;
993 
994 		if (!TinselV2) {
995 			/*-------------------------
996 			 Change of path polygon?  |
997 			 -------------------------*/
998 			hPath = InPolygon(x, y, PATH);
999 			if (pMover->hCpath != hPath &&
1000 			   !IsInPolygon(x, y, pMover->hCpath) &&
1001 			   !IsAdjacentPath(pMover->hCpath, pMover->hIpath)) {
1002 				/*----------------------------------------------------------
1003 				 If just entering a follow nodes polygon, go to first node.|
1004 				 Else if just going to pass through, go to pseudo-center.  |
1005 				 ----------------------------------------------------------*/
1006 				if (PolySubtype(hPath) == NODE && pMover->hFnpath != hPath && pMover->npstatus != LEAVING) {
1007 					int node = NearestEndNode(hPath, x, y);
1008 					getNpathNode(hPath, node, &nx, &ny);
1009 					SetMoverDest(pMover, nx, ny);
1010 					pMover->over = true;
1011 				} else if (!IsInPolygon(pMover->ItargetX, pMover->ItargetY, hPath) &&
1012 					!IsInPolygon(pMover->ItargetX, pMover->ItargetY, pMover->hCpath)) {
1013 					SetMoverDest(pMover, PolyCenterX(hPath), PolyCenterY(hPath));
1014 					pMover->over = true;
1015 				} else {
1016 					SetMoverDest(pMover, pMover->ItargetX, pMover->ItargetY);
1017 				}
1018 				break;
1019 			}
1020 
1021 			lstatus = s1;
1022 		}
1023 	}
1024 }
1025 
1026 /**
1027  * Work out where the next position should be.
1028  * Check that it's in a path and not in a blocking polygon.
1029  */
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)1030 static void NewCoOrdinates(int fromx, int fromy, int *targetX, int *targetY,
1031 				int *newx, int *newy, int *s1, int *s2,
1032 				HPOLYGON *hS2p, bool bOver, bool bBodge,
1033 				PMOVER pMover, PMOVER *collisionActor) {
1034 	HPOLYGON hPoly;
1035 	int sidem, depthm;
1036 	int sidesteps, depthsteps;
1037 	PMOVER	ma;
1038 
1039 	*s1 = *s2 = 0;
1040 
1041 	/*------------------------------------------------
1042 	 Don't overrun if this is the final destination. |
1043 	 ------------------------------------------------*/
1044 	if ((*targetX == pMover->UtargetX && (*targetY == -1 || *targetY == pMover->UtargetY)) ||
1045 		(*targetY == pMover->UtargetY && (*targetX == -1 || *targetX == pMover->UtargetX)))
1046 		bOver = false;
1047 
1048 	/*----------------------------------------------------
1049 	 Decide how big a step to attempt in each direction. |
1050 	 ----------------------------------------------------*/
1051 	sidesteps = *targetX == -1 ? 0 : *targetX - fromx;
1052 	sidesteps = ABS(sidesteps);
1053 
1054 	depthsteps = *targetY == -1 ? 0 : *targetY - fromy;
1055 	depthsteps = ABS(depthsteps);
1056 
1057 	if (sidesteps && depthsteps > sidesteps) {
1058 		depthm = YMDIST;
1059 		sidem = depthm * sidesteps/depthsteps;
1060 
1061 		if (!sidem)
1062 			sidem = 1;
1063 	} else if (depthsteps && sidesteps > depthsteps) {
1064 		sidem = XMDIST;
1065 		depthm = sidem * depthsteps/sidesteps;
1066 
1067 		if (!depthm) {
1068 			if (bBodge)
1069 				depthm = 1;
1070 		} else if (depthm > YMDIST)
1071 			depthm = YMDIST;
1072 	} else {
1073 		sidem = sidesteps ? XMDIST : 0;
1074 		depthm = depthsteps ? YMDIST : 0;
1075 	}
1076 
1077 	*newx = fromx;
1078 	*newy = fromy;
1079 
1080 	/*------------------------------------------------------------
1081 	 If Left-Right movement is required - then make the move,    |
1082 	 but don't overshoot, and do notice when we're already there |
1083 	 ------------------------------------------------------------*/
1084 	if (*targetX == -1)
1085 		*s1 |= XTHERE;
1086 	else {
1087 		if (*targetX > fromx) {		/* To the right?	*/
1088 			*newx += sidem;		// Move to the right...
1089 			if (*newx == *targetX)
1090 				*s1 |= XTHERE;
1091 			else if (*newx > *targetX) {	// ...but don't overshoot
1092 				if (!bOver)
1093 					*newx = *targetX;
1094 				else
1095 					*targetX = *newx;
1096 				*s1 |= XTHERE;
1097 			}
1098 		} else if (*targetX < fromx) {	/* To the left?		*/
1099 			*newx -= sidem;		// Move to the left...
1100 			if (*newx == *targetX)
1101 				*s1 |= XTHERE;
1102 			else if (*newx < *targetX) {	// ...but don't overshoot
1103 				if (!bOver)
1104 					*newx = *targetX;
1105 				else
1106 					*targetX = *newx;
1107 				*s1 |= XTHERE;
1108 			}
1109 		} else {
1110 			*targetX = -1;		// We're already there!
1111 			*s1 |= XTHERE;
1112 		}
1113 	}
1114 
1115 	/*--------------------------------------------------------------
1116 	 If Up-Down movement is required - then make the move,
1117 	 but don't overshoot, and do notice when we're already there
1118 	 --------------------------------------------------------------*/
1119 	if (*targetY == -1)
1120 		*s1 |= YTHERE;
1121 	else {
1122 		if (*targetY > fromy) {		/* Downwards?		*/
1123 			*newy += depthm;	// Move down...
1124 			if (*newy == *targetY)	// ...but don't overshoot
1125 				*s1 |= YTHERE;
1126 			else if (*newy > *targetY) {	// ...but don't overshoot
1127 				if (!bOver)
1128 					*newy = *targetY;
1129 				else
1130 					*targetY = *newy;
1131 				*s1 |= YTHERE;
1132 			}
1133 		} else if (*targetY < fromy) {	/* Upwards?		*/
1134 			*newy -= depthm;	// Move up...
1135 			if (*newy == *targetY)	// ...but don't overshoot
1136 				*s1 |= YTHERE;
1137 			else if (*newy < *targetY) {	// ...but don't overshoot
1138 				if (!bOver)
1139 					*newy = *targetY;
1140 				else
1141 					*targetY = *newy;
1142 				*s1 |= YTHERE;
1143 			}
1144 		} else {
1145 			*targetY = -1;		// We're already there!
1146 			*s1 |= YTHERE;
1147 		}
1148 	}
1149 
1150 	/* Give over if this is it */
1151 	if (*s1 == (XTHERE | YTHERE))
1152 		return;
1153 
1154 	/*------------------------------------------------------
1155 	 Have worked out where an optimum step would take us.
1156 	 Must now check if it's in a legal spot.
1157 	 ------------------------------------------------------*/
1158 
1159 	if (!pMover->bNoPath && !pMover->bIgPath) {
1160 		/*------------------------------
1161 		 Must stay in a path polygon.
1162 		-------------------------------*/
1163 		hPoly = InPolygon(*newx, *newy, PATH);
1164 		if (hPoly == NOPOLY) {
1165 			*s2 = LEAVING_PATH;	// Trying to leave the path polygons
1166 
1167 			if (*newx != fromx && InPolygon(*newx, fromy, PATH) != NOPOLY && InPolygon(*newx, fromy, BLOCK) == NOPOLY) {
1168 				*newy = fromy;
1169 				*s1 |= YRESTRICT;
1170 			} else if (*newy != fromy && InPolygon(fromx, *newy, PATH) != NOPOLY && InPolygon(fromx, *newy, BLOCK) == NOPOLY) {
1171 				*newx = fromx;
1172 				*s1 |= XRESTRICT;
1173 			} else {
1174 				*newx = fromx;
1175 				*newy = fromy;
1176 #if 1
1177 				*targetX = *targetY = -1;
1178 #endif
1179 				*s1 |= STUCK;
1180 				return;
1181 			}
1182 		}
1183 
1184 		/*--------------------------------------
1185 		 Must stay out of blocking polygons.
1186 		 --------------------------------------*/
1187 		hPoly = InPolygon(*newx, *newy, BLOCK);
1188 		if (hPoly != NOPOLY) {
1189 			*s2 = ENTERING_BLOCK;	// Trying to enter a blocking poly
1190 			*hS2p = hPoly;
1191 
1192 			if (*newx != fromx && InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
1193 				*newy = fromy;
1194 				*s1 |= YRESTRICT;
1195 			} else if (*newy != fromy && InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
1196 				*newx = fromx;
1197 				*s1 |= XRESTRICT;
1198 			} else {
1199 				*newx = fromx;
1200 				*newy = fromy;
1201 #if 1
1202 				*targetX = *targetY = -1;
1203 #endif
1204 				*s1 |= STUCK;
1205 			}
1206 		}
1207 		/*------------------------------------------------------
1208 		 Must stay out of moving actors' blocking polygons.
1209 		 ------------------------------------------------------*/
1210 		ma = InMoverBlock(pMover, *newx, *newy);
1211 		if (ma != NULL) {
1212 			// Ignore if already in it (it may have just appeared)
1213 			if (!InMoverBlock(pMover, pMover->objX, pMover->objY)) {
1214 				*s2 = ENTERING_MBLOCK;	// Trying to walk through an actor
1215 
1216 				*hS2p = -1;
1217 				if (collisionActor)
1218 					*collisionActor = ma;
1219 
1220 				if (*newx != fromx && InMoverBlock(pMover, *newx, fromy) == NULL
1221 				    && InPolygon(*newx, fromy, BLOCK) == NOPOLY && InPolygon(*newx, fromy, PATH) != NOPOLY) {
1222 					*newy = fromy;
1223 					*s1 |= YRESTRICT;
1224 				} else if (*newy != fromy && InMoverBlock(pMover, fromx, *newy) == NULL
1225 				           && InPolygon(fromx, *newy, BLOCK) == NOPOLY && InPolygon(fromx, *newy, PATH) != NOPOLY) {
1226 					*newx = fromx;
1227 					*s1 |= XRESTRICT;
1228 				} else {
1229 					*newx = fromx;
1230 					*newy = fromy;
1231 #if 1
1232 					*targetX = *targetY = -1;
1233 #endif
1234 					*s1 |= STUCK;
1235 				}
1236 			}
1237 		}
1238 	}
1239 }
1240 
1241 /**
1242  * SetOffWithinNodePath
1243  */
SetOffWithinNodePath(PMOVER pMover,HPOLYGON StartPath,HPOLYGON DestPath,int targetX,int targetY)1244 static void SetOffWithinNodePath(PMOVER pMover, HPOLYGON StartPath, HPOLYGON DestPath,
1245 								 int targetX, int targetY) {
1246 	int endnode;
1247 	HPOLYGON hIpath;
1248 	int	nx, ny;
1249 	int	x, y;
1250 
1251 	if (StartPath == DestPath) {
1252 		if (pMover->line == pMover->Tline) {
1253 			SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
1254 			pMover->over = false;
1255 		} else if (pMover->line < pMover->Tline) {
1256 			getNpathNode(StartPath, pMover->line+1, &nx, &ny);
1257 			SetMoverDest(pMover, nx, ny);
1258 			pMover->npstatus = GOING_UP;
1259 		} else if (pMover->line > pMover->Tline) {
1260 			getNpathNode(StartPath, pMover->line, &nx, &ny);
1261 			SetMoverDest(pMover, nx, ny);
1262 			pMover->npstatus = GOING_DOWN;
1263 		}
1264 	} else {
1265 		/*
1266 		 * Leaving this path - work out
1267 		 * which end of this path to head for.
1268 		 */
1269 		assert(DestPath != NOPOLY); // Error 702
1270 		if ((hIpath = GetPathOnTheWay(StartPath, DestPath)) == NOPOLY) {
1271 			// This should never happen!
1272 			// It's the old code that didn't always work.
1273 			endnode = NearestEndNode(StartPath, targetX, targetY);
1274 		} else {
1275 			if (PolySubtype(hIpath) != NODE) {
1276 				x = PolyCenterX(hIpath);
1277 				y = PolyCenterY(hIpath);
1278 				endnode = NearestEndNode(StartPath, x, y);
1279 			} else {
1280 				endnode = NearEndNode(StartPath, hIpath);
1281 			}
1282 		}
1283 
1284 #if 1
1285 		if ((pMover->npstatus == LEAVING) &&
1286 			endnode == NearestEndNode(StartPath, pMover->objX, pMover->objY)) {
1287 			// Leave it be
1288 
1289 			if (TinselV2) {
1290 				// Yeah, but we need a destination
1291 				// It's release night and there's this problem in the bar...
1292 				if (hIpath)	// must be, but...
1293 				{
1294 					// could go for its end node if it's an NPATH
1295 					// but we probably will when we hit it anyway!
1296 					SetMoverDest(pMover, PolyCenterX(hIpath), PolyCenterY(hIpath));
1297 				}
1298 			}
1299 		} else
1300 #endif
1301 		{
1302 			if (endnode) {
1303 				getNpathNode(StartPath, pMover->line+1, &nx, &ny);
1304 				SetMoverDest(pMover, nx, ny);
1305 				pMover->npstatus = GOING_UP;
1306 			} else {
1307 				getNpathNode(StartPath, pMover->line, &nx, &ny);
1308 				SetMoverDest(pMover, nx, ny);
1309 				pMover->npstatus = GOING_DOWN;
1310 			}
1311 		}
1312 	}
1313 }
1314 
1315 /**
1316  * Restore a movement, called from restoreMovement() in ACTORS.CPP
1317  */
SSetActorDest(PMOVER pActor)1318 void SSetActorDest(PMOVER pActor) {
1319 	if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
1320 		Stand(Common::nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
1321 
1322 		if (pActor->UtargetX != -1 && pActor->UtargetY != -1) {
1323 			SetActorDest(pActor, pActor->UtargetX, pActor->UtargetY,
1324 					pActor->bIgPath, 0);
1325 		}
1326 	} else {
1327 		Stand(Common::nullContext, pActor->actorID, pActor->objX, pActor->objY, 0);
1328 	}
1329 }
1330 
1331 /**
1332  * Initiate a movement, called from WalkTo_Event()
1333  */
SetActorDest(PMOVER pMover,int clickX,int clickY,bool igPath,SCNHANDLE hFilm)1334 int SetActorDest(PMOVER pMover, int clickX, int clickY, bool igPath, SCNHANDLE hFilm) {
1335 	HPOLYGON StartPath, DestPath = 0;
1336 	int targetX, targetY;
1337 
1338 	if (TinselV2) {
1339 		// No need to synchronise if not moving!
1340 		// Hopefully will stop luggage flip in shades.
1341 		if (!MoverMoving(pMover))
1342 			pMover->stepCount = 0;
1343 
1344 		// Fix interrupted-walking-to-wardrobe bug in mortuary
1345 		StopMover(pMover);
1346 	} else {
1347 		if (pMover->actorID == _vm->_actor->GetLeadId()) // Now only for lead actor
1348 			_vm->_actor->UnTagActor(pMover->actorID);    // Tag not allowed while moving
1349 	}
1350 
1351 	pMover->walkNumber++;
1352 	pMover->bStop = false;
1353 	pMover->over = false;
1354 	pMover->walkedFromX = pMover->objX;
1355 	pMover->walkedFromY = pMover->objY;
1356 	pMover->bMoving = true;
1357 	pMover->bIgPath = igPath;
1358 	pMover->zOverride = -1;
1359 	pMover->hRpath = NOPOLY;
1360 
1361 	if (!TinselV2) {
1362 		// Use the supplied reel or restore the normal actor.
1363 		if (hFilm != 0)
1364 			AlterMover(pMover, hFilm, AR_WALKREEL);
1365 		else
1366 			AlterMover(pMover, 0, AR_NORMAL);
1367 	}
1368 
1369 	if (igPath) {
1370 		targetX = clickX;
1371 		targetY = clickY;
1372 
1373 		if (pMover->actorID == _vm->_actor->GetLeadId()) {
1374 			g_lastLeadXdest = targetX;
1375 			g_lastLeadYdest = targetY;
1376 		}
1377 	} else {
1378 		int wodResult = WorkOutDestination(clickX, clickY, &targetX, &targetY);
1379 
1380 		if (pMover->actorID == _vm->_actor->GetLeadId()) {
1381 			g_lastLeadXdest = targetX;
1382 			g_lastLeadYdest = targetY;
1383 		}
1384 
1385 		if (wodResult == ALL_SORTED) {
1386 			GotThere(pMover);
1387 			return 0;
1388 		}
1389 		assert(InPolygon(targetX, targetY, PATH) != NOPOLY); // illegal destination!
1390 		assert(InPolygon(targetX, targetY, BLOCK) == NOPOLY); // illegal destination!
1391 	}
1392 
1393 
1394 	/***** Now have a destination to aim for. *****/
1395 
1396 	/*----------------------------------
1397 	| Don't move if it's not worth it.
1398 	----------------------------------*/
1399 	if (ABS(targetX - pMover->objX) < XMDIST && ABS(targetY - pMover->objY) < YMDIST) {
1400 		GotThere(pMover);
1401 		return 0;
1402 	}
1403 
1404 	/*------------------------------------------------------
1405 	| If the destiation is within a follow nodes polygon,
1406 	| set destination as the nearest node.
1407 	------------------------------------------------------*/
1408 	if (!igPath) {
1409 		DestPath = InPolygon(targetX, targetY, PATH);
1410 		if (PolySubtype(DestPath) == NODE) {
1411 			// Find the nearest point on a line, or nearest node
1412 			FindBestPoint(DestPath, &targetX, &targetY, &pMover->Tline);
1413 		}
1414 	}
1415 
1416 	assert(pMover->bIgPath || InPolygon(targetX, targetY, PATH) != NOPOLY); // Error 5005
1417 	SetMoverUltDest(pMover, targetX, targetY);
1418 	SetMoverIntDest(pMover, targetX, targetY);
1419 
1420 	if (TinselV2) {
1421 		// No movement for unconnected paths
1422 		if (pMover->hIpath == NOPOLY && !igPath) {
1423 			GotThere(pMover);
1424 			return 0;
1425 		}
1426 
1427 		// Use the supplied reel or restore the normal actor.
1428 		if (hFilm != 0)
1429 			AlterMover(pMover, hFilm, AR_WALKREEL);
1430 		else
1431 			AlterMover(pMover, 0, AR_NORMAL);
1432 	}
1433 
1434 	/*-------------------------------------------------------------------
1435 	| If in a follow nodes path, need to set off in the right direction! |
1436 	-------------------------------------------------------------------*/
1437 	if ((StartPath = pMover->hFnpath) != NOPOLY && !igPath) {
1438 		SetOffWithinNodePath(pMover, StartPath, DestPath, targetX, targetY);
1439 	} else {
1440 		// Set off!
1441 		SetNextDest(pMover);
1442 	}
1443 
1444 	return pMover->walkNumber;
1445 }
1446 
1447 /**
1448  * Change scale if appropriate.
1449  */
CheckScale(PMOVER pActor,HPOLYGON hPath,int ypos)1450 static void CheckScale(PMOVER pActor, HPOLYGON hPath, int ypos) {
1451 	int scale;
1452 
1453 	scale = GetScale(hPath, ypos);
1454 	if (scale != pActor->scale) {
1455 		SetMoverWalkReel(pActor, pActor->direction, scale, false);
1456 	}
1457 }
1458 
1459 /**
1460  * Not going anywhere - Kick off again if not at final destination.
1461  */
NotMoving(PMOVER pActor,int x,int y)1462 static void NotMoving(PMOVER pActor, int x, int y) {
1463 	pActor->targetX = pActor->targetY = -1;
1464 
1465 //	if (x == pActor->UtargetX && y == pActor->UtargetY)
1466 	if (ABS(x - pActor->UtargetX) < XMDIST && ABS(y - pActor->UtargetY) < YMDIST) {
1467 		GotThere(pActor);
1468 		return;
1469 	}
1470 
1471 	if (pActor->ItargetX != -1 || pActor->ItargetY != -1) {
1472 		SetNextDest(pActor);
1473 	} else if (pActor->UtargetX != -1 || pActor->UtargetY != -1) {
1474 		assert(pActor->bIgPath || InPolygon(pActor->UtargetX, pActor->UtargetY, PATH) != NOPOLY); // Error 5006
1475 		SetMoverIntDest(pActor, pActor->UtargetX, pActor->UtargetY);
1476 		SetNextDest(pActor);
1477 	}
1478 }
1479 
1480 /**
1481  * Does the necessary business when entering a different path polygon.
1482  */
EnteringNewPath(PMOVER pMover,HPOLYGON hPath,int x,int y)1483 static void EnteringNewPath(PMOVER pMover, HPOLYGON hPath, int x, int y) {
1484 	int	firstnode;	// First node to go to
1485 	int	lastnode;	// Last node to go to
1486 	HPOLYGON hIpath;
1487 	HPOLYGON hLpath;	// one we're leaving
1488 	int	nx, ny;
1489 	int	nxl, nyl;
1490 
1491 	hLpath = pMover->hCpath;
1492 	pMover->hCpath = hPath;		// current path
1493 
1494 	if (hPath == NOPOLY) {
1495 		// Not proved this ever happens, but just in case
1496 		pMover->hFnpath = NOPOLY;
1497 		pMover->npstatus = NOT_IN;
1498 		return;
1499 	}
1500 
1501 	// Is new path a node path?
1502 	if (PolySubtype(hPath) == NODE) {
1503 		// Node path - usually go to nearest end node
1504 		firstnode = NearestEndNode(hPath, x, y);
1505 		lastnode = -1;
1506 
1507 		// If this is not the destination path,
1508 		// find which end nodfe we wish to leave via
1509 		if (hPath != pMover->hUpath) {
1510 			if (pMover->bIgPath) {
1511 				lastnode = NearestEndNode(hPath, pMover->UtargetX, pMover->UtargetY);
1512 			} else {
1513 				assert(pMover->hUpath != NOPOLY); // Error 703
1514 				hIpath = GetPathOnTheWay(hPath, pMover->hUpath);
1515 				assert(hIpath != NOPOLY); // No path on the way
1516 
1517 				if (PolySubtype(hIpath) != NODE) {
1518 					lastnode = NearestEndNode(hPath, PolyCenterX(hIpath), PolyCenterY(hIpath));
1519 				} else {
1520 					lastnode = NearEndNode(hPath, hIpath);
1521 				}
1522 			}
1523 		}
1524 		// Test for pseudo-one-node npaths
1525 		if (lastnode != -1 && numNodes(hPath) == 2) {
1526 			getNpathNode(hPath, firstnode, &nx, &ny);
1527 			getNpathNode(hPath, lastnode, &nxl, &nyl);
1528 			if (nxl == nx && nyl == ny)
1529 				firstnode = lastnode;
1530 		}
1531 
1532 		// If leaving by same node as entering, don't bother.
1533 		if (lastnode == firstnode) {
1534 			pMover->hFnpath = NOPOLY;
1535 			pMover->npstatus = NOT_IN;
1536 			assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5007
1537 			SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
1538 			SetNextDest(pMover);
1539 		} else {
1540 			// Head for first node
1541 			pMover->over = true;
1542 			pMover->npstatus = ENTERING;
1543 			pMover->hFnpath = hPath;
1544 			pMover->line = firstnode ? firstnode - 1 : firstnode;
1545 			if (pMover->line == pMover->Tline && hPath == pMover->hUpath) {
1546 				assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5008
1547 				SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
1548 				SetMoverDest(pMover, pMover->UtargetX, pMover->UtargetY);
1549 			} else {
1550 				// This doesn't seem right
1551 				getNpathNode(hPath, firstnode, &nx, &ny);
1552 				if (ABS(pMover->objX - nx) < XMDIST
1553 						&& ABS(pMover->objY - ny) < YMDIST) {
1554 					pMover->npstatus = ENTERING;
1555 					pMover->hFnpath = hPath;
1556 					SetNextDest(pMover);
1557 				} else {
1558 					getNpathNode(hPath, firstnode, &nx, &ny);
1559 					SetMoverDest(pMover, nx, ny);
1560 				}
1561 			}
1562 		}
1563 		return;
1564 	} else {
1565 		pMover->hFnpath = NOPOLY;
1566 		pMover->npstatus = NOT_IN;
1567 		assert(pMover->bIgPath || InPolygon(pMover->UtargetX, pMover->UtargetY, PATH) != NOPOLY); // Error 5009
1568 
1569 		// Added 26/01/95
1570 		if (IsPolyCorner(hPath, pMover->ItargetX, pMover->ItargetY))
1571 			return;
1572 
1573 		// Added 23/10/96
1574 		if (TinselV2 && (pMover->hRpath == hPath))
1575 			return;
1576 
1577 		if (TinselV2)
1578 			pMover->hRpath = hLpath;
1579 		SetMoverIntDest(pMover, pMover->UtargetX, pMover->UtargetY);
1580 		SetNextDest(pMover);
1581 	}
1582 }
1583 
1584 /**
1585  * Move
1586  */
Move(PMOVER pMover,int newx,int newy,HPOLYGON hPath)1587 void Move(PMOVER pMover, int newx, int newy, HPOLYGON hPath) {
1588 	pMover->objX = newx;
1589 	pMover->objY = newy;
1590 
1591 	MultiSetAniXY(pMover->actorObj, newx, newy);
1592 	SetMoverZ(pMover, newy, GetPolyZfactor(hPath));
1593 	if (StepAnimScript(&pMover->actorAnim) == ScriptFinished) {
1594 		// The end of a scale-change reel
1595 		// Revert to normal walking reel
1596 		pMover->bWalkReel = false;
1597 		pMover->stepCount = 0;
1598 		SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
1599 	}
1600 
1601 	// Synchronised walking reels
1602 	if (++pMover->stepCount >= STEPS_MAX)
1603 		pMover->stepCount = 0;
1604 }
1605 
1606 /**
1607  * Called from MActorProcess() on every tick.
1608  *
1609  * Moves the actor as appropriate.
1610  */
MoveActor(PMOVER pMover)1611 void MoveActor(PMOVER pMover) {
1612 	int newx, newy;
1613 	HPOLYGON hPath;
1614 	int status, s2;		// s2 not used here!
1615 	HPOLYGON hS2p;		// nor is s2p!
1616 	HPOLYGON hEb;
1617 	PMOVER ma;
1618 	int	sTargetX, sTargetY;
1619 	bool bNewPath = false;
1620 
1621 	// Only do anything if the actor needs to move!
1622 	if (pMover->targetX == -1 && pMover->targetY == -1)
1623 		return;
1624 
1625 	if (pMover->bStop) {
1626 		GotThere(pMover);
1627 		pMover->bStop = false;
1628 		pMover->walkNumber++;
1629 		SetMoverStanding(pMover);
1630 		return;
1631 	}
1632 
1633 #if SLOW_RINCE_DOWN
1634 /**/	if (g_BogusVar++ < g_Interlude)	// Temporary slow-down-the-action code
1635 /**/		return;			//
1636 /**/	g_BogusVar = 0;			//
1637 #endif
1638 
1639 	if (!TinselV2) {
1640 		// During swalk()s, movement while hidden may be slowed down.
1641 		if (pMover->bHidden) {
1642 			if (++g_hSlowVar < pMover->SlowFactor)
1643 				return;
1644 			g_hSlowVar = 0;
1645 		}
1646 	}
1647 
1648 	// 'push' the target
1649 	sTargetX = pMover->targetX;
1650 	sTargetY = pMover->targetY;
1651 
1652 	NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY,
1653 			&newx, &newy, &status, &s2, &hS2p, pMover->over, false, pMover);
1654 
1655 	if (newx == pMover->objX && newy == pMover->objY) {
1656 		// 'pop' the target
1657 		pMover->targetX = sTargetX;
1658 		pMover->targetY = sTargetY;
1659 
1660 		NewCoOrdinates(pMover->objX, pMover->objY, &pMover->targetX, &pMover->targetY, &newx, &newy,
1661 				&status, &s2, &hS2p, pMover->over, true, pMover);
1662 		if (newx == pMover->objX && newy == pMover->objY) {
1663 			NotMoving(pMover, newx, newy);
1664 			return;
1665 		}
1666 	}
1667 
1668 	// Find out which path we're in now
1669 	hPath = InPolygon(newx, newy, PATH);
1670 	if (hPath == NOPOLY) {
1671 		if (pMover->bNoPath) {
1672 			Move(pMover, newx, newy, pMover->hCpath);
1673 			return;
1674 		} else {
1675 			// May be marginally outside!
1676 			// OR bIgPath may be set.
1677 			hPath = pMover->hCpath;
1678 		}
1679 	} else if (pMover->bNoPath) {
1680 		pMover->bNoPath = false;
1681 		bNewPath = true;
1682 	} else if (hPath != pMover->hCpath) {
1683 		if (IsInPolygon(newx, newy, pMover->hCpath))
1684 			hPath = pMover->hCpath;
1685 	}
1686 
1687 	CheckScale(pMover, hPath, newy);
1688 
1689 	/*
1690 	* Must stay out of moving actors' blocking polygons.
1691 	*/
1692 	ma = InMoverBlock(pMover, newx, newy);
1693 	if (ma != NULL) {
1694 		// Stop if there's no chance of arriving
1695 		if (InMoverBlock(pMover, pMover->UtargetX, pMover->UtargetY)) {
1696 			GotThere(pMover);
1697 			return;
1698 		}
1699 
1700 		if (InMoverBlock(pMover, pMover->objX, pMover->objY))
1701 			;
1702 		else {
1703 			hEb = InitExtraBlock(pMover, ma);
1704 			newx = pMover->objX;
1705 			newy = pMover->objY;
1706 			BlockingCorner(hEb, &newx, &newy, pMover->ItargetX, pMover->ItargetY);
1707 			SetMoverDest(pMover, newx, newy);
1708 			return;
1709 		}
1710 	}
1711 
1712 	/*--------------------------------------
1713 	 This is where it actually gets moved.
1714 	 --------------------------------------*/
1715 	Move(pMover, newx, newy, hPath);
1716 
1717 	// Entering a new path polygon?
1718 	if (hPath != pMover->hCpath || bNewPath)
1719 		EnteringNewPath(pMover, hPath, newx, newy);
1720 }
1721 
1722 /**
1723  * Store the default refer type for the current scene.
1724  */
SetDefaultRefer(int32 defRefer)1725 void SetDefaultRefer(int32 defRefer) {
1726 	g_DefaultRefer = defRefer;
1727 }
1728 
GetLastLeadXdest()1729 int GetLastLeadXdest() {
1730 	return g_lastLeadXdest;
1731 }
1732 
GetLastLeadYdest()1733 int GetLastLeadYdest() {
1734 	return g_lastLeadYdest;
1735 }
1736 
1737 
1738 
1739 
1740 /**
1741  * DoMoveActor
1742  */
DoMoveActor(PMOVER pActor)1743 void DoMoveActor(PMOVER pActor) {
1744 	int wasx, wasy;
1745 	int i;
1746 
1747 #define NUMBER 1
1748 
1749 	wasx = pActor->objX;
1750 	wasy = pActor->objY;
1751 
1752 	MoveActor(pActor);
1753 
1754 	if ((pActor->targetX != -1 || pActor->targetY != -1)
1755 	&&  (wasx == pActor->objX && wasy == pActor->objY))	{
1756 		for (i=0; i < NUMBER; i++) {
1757 			MoveActor(pActor);
1758 			if (wasx != pActor->objX || wasy != pActor->objY)
1759 				break;
1760 		}
1761 //		assert(i<NUMBER);
1762 	}
1763 }
1764 
1765 } // End of namespace Tinsel
1766