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