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