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