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
22 #include "tinsel/actors.h"
23 #include "tinsel/font.h"
24 #include "tinsel/handle.h"
25 #include "tinsel/pcode.h"
26 #include "tinsel/pid.h"
27 #include "tinsel/polygons.h"
28 #include "tinsel/rince.h"
29 #include "tinsel/sched.h"
30 #include "common/serializer.h"
31 #include "tinsel/tinsel.h"
32 #include "tinsel/token.h"
33
34 #include "common/textconsole.h"
35 #include "common/util.h"
36
37 namespace Tinsel {
38
39
40 //----------------- LOCAL DEFINES --------------------
41
42 /** different types of polygon */
43 enum POLY_TYPE {
44 POLY_PATH, POLY_NPATH, POLY_BLOCK, POLY_REFER, POLY_EFFECT,
45 POLY_EXIT, POLY_TAG
46 };
47
48 // Note 7/10/94, with adjacency reduction ANKHMAP max is 3, UNSEEN max is 4
49 // so reduced this back to 6 (from 12) for now.
50 #define MAXADJ 6 // Max number of known adjacent paths
51
52 struct POLYGON {
53
54 PTYPE polyType; // Polygon type
55
56 int subtype; // refer type in REFER polygons
57 // NODE/NORMAL in PATH polygons
58
59 int pIndex; // Index into compiled polygon data
60
61 /*
62 * Data duplicated from compiled polygon data
63 */
64 short cx[4]; // Corners (clockwise direction)
65 short cy[4];
66 int polyID;
67
68 /* For TAG polygons only */
69 int tagFlags;
70 SCNHANDLE hOverrideTag;
71
72 /* For TAG and EXIT (and EFFECT in future?) polygons only */
73 TSTATE tagState;
74 PSTATE pointState;
75
76 /* For Path polygons only */
77 bool tried;
78
79 /*
80 * Internal derived data for speed and conveniance
81 * set up by FiddlyBit()
82 */
83 short ptop; //
84 short pbottom; // Enclosing external rectangle
85 short pleft; //
86 short pright; //
87
88 short ltop[4]; //
89 short lbottom[4]; // Rectangles enclosing each side
90 short lleft[4]; //
91 short lright[4]; //
92
93 int a[4]; // y1-y2 }
94 int b[4]; // x2-x1 } See IsInPolygon()
95 long c[4]; // y1x2 - x1y2 }
96
97 /*
98 * Internal derived data for speed and conveniance
99 * set up by PseudoCenter()
100 */
101 int pcenterx; // Pseudo-center
102 int pcentery; //
103
104 /**
105 * List of adjacent polygons. For Path polygons only.
106 * set up by SetPathAdjacencies()
107 */
108 POLYGON *adjpaths[MAXADJ];
109
110 };
111 typedef POLYGON *PPOLYGON;
112
113 #define MAXONROUTE 40
114
115 #include "common/pack-start.h" // START STRUCT PACKING
116
117 /** lineinfo struct - one per (node-1) in a node path */
118 struct LINEINFO {
119
120 int32 a;
121 int32 b;
122 int32 c;
123
124 int32 a2; ///< a squared
125 int32 b2; ///< b squared
126 int32 a2pb2; ///< a squared + b squared
127 int32 ra2pb2; ///< root(a squared + b squared)
128
129 int32 ab;
130 int32 ac;
131 int32 bc;
132 } PACKED_STRUCT;
133
134 #include "common/pack-end.h" // END STRUCT PACKING
135
136 /**
137 * POLY structure class. This is implemented as a class, because the structure
138 * of POLY's changed between TINSEL v1 and v2.
139 *
140 * FIXME: Right now, we always read *all* data in a polygon, even if only a single
141 * field is needed. This is rather inefficient.
142 */
143 class Poly {
144 private:
145 const byte * const _pStart;
146 const byte *_pData;
147 int _recordSize;
148 void nextPoly();
149
150 public:
151 Poly(const byte *pSrc);
152 Poly(const byte *pSrc, int startIndex);
153 void operator++();
154 void setIndex(int index);
155
156
getType() const157 POLY_TYPE getType() const { return (POLY_TYPE)FROM_32(type); }
getNodecount() const158 int getNodecount() const { return (int)FROM_32(nodecount); }
getNodeX(int i) const159 int getNodeX(int i) const { return (int)FROM_32(nlistx[i]); }
getNodeY(int i) const160 int getNodeY(int i) const { return (int)FROM_32(nlisty[i]); }
161
162 // get Inter-node line structure
getLineinfo(int i) const163 const LINEINFO *getLineinfo(int i) const { return ((const LINEINFO *)(_pStart + (int)FROM_32(plinelist))) + i; }
164
165 protected:
166 POLY_TYPE type; ///< type of polygon
167 public:
168 int32 x[4], y[4]; // Polygon definition
169 uint32 xoff, yoff; // DW2 - polygon offset
170
171 int32 tagx, tagy; // } For tagged polygons
172 SCNHANDLE hTagtext; // } i.e. EXIT, TAG, EFFECT
173
174 int32 nodex, nodey; // EXIT, TAG, REFER
175 SCNHANDLE hFilm; ///< film reel handle for EXIT, TAG
176
177 int32 reftype; ///< Type of REFER
178
179 int32 id; // } EXIT and TAG
180
181 int32 scale1, scale2; // }
182 int32 level1, level2; // DW2 fields
183 int32 bright1, bright2; // DW2 fields
184 int32 reel; // } PATH and NPATH
185 int32 zFactor; // }
186
187 protected:
188 int32 nodecount; ///<The number of nodes in this polygon
189 int32 pnodelistx, pnodelisty; ///<offset in chunk to this array if present
190 int32 plinelist;
191
192 const int32 *nlistx;
193 const int32 *nlisty;
194
195 public:
196 SCNHANDLE hScript; ///< handle of code segment for polygon events
197 };
198
Poly(const byte * pSrc)199 Poly::Poly(const byte *pSrc) : _pStart(pSrc) {
200 _pData = pSrc;
201 nextPoly();
202 _recordSize = _pData - pSrc;
203 }
204
Poly(const byte * pSrc,int startIndex)205 Poly::Poly(const byte *pSrc, int startIndex) : _pStart(pSrc) {
206 _pData = pSrc;
207 nextPoly();
208 _recordSize = _pData - pSrc;
209 setIndex(startIndex);
210 }
211
operator ++()212 void Poly::operator++() {
213 nextPoly();
214 }
215
setIndex(int index)216 void Poly::setIndex(int index) {
217 _pData = _pStart + index * _recordSize;
218 nextPoly();
219 }
220
nextLong(const byte * & p)221 static uint32 nextLong(const byte *&p) {
222 uint32 result = READ_UINT32(p);
223 p += 4;
224 return result;
225 }
226
nextPoly()227 void Poly::nextPoly() {
228 // Note: For now we perform no endian conversion of the data. We could change that
229 // at some point, and remove all endian conversions from the code that uses POLY's
230 const byte *pRecord = _pData;
231
232 int typeVal = nextLong(_pData);
233 if ((FROM_32(typeVal) == 5) && TinselV2)
234 typeVal = TO_32(6);
235 type = (POLY_TYPE)typeVal;
236
237 for (int i = 0; i < 4; ++i)
238 x[i] = nextLong(_pData);
239 for (int i = 0; i < 4; ++i)
240 y[i] = nextLong(_pData);
241
242 if (TinselV2) {
243 xoff = nextLong(_pData);
244 yoff = nextLong(_pData);
245 id = nextLong(_pData);
246 reftype = nextLong(_pData);
247 }
248
249 tagx = nextLong(_pData);
250 tagy = nextLong(_pData);
251 hTagtext = nextLong(_pData);
252 nodex = nextLong(_pData);
253 nodey = nextLong(_pData);
254 hFilm = nextLong(_pData);
255
256 if (!TinselV2) {
257 reftype = nextLong(_pData);
258 id = nextLong(_pData);
259 }
260
261 scale1 = nextLong(_pData);
262 scale2 = nextLong(_pData);
263
264 if (TinselV2) {
265 level1 = nextLong(_pData);
266 level2 = nextLong(_pData);
267 bright1 = nextLong(_pData);
268 bright2 = nextLong(_pData);
269 }
270
271 reel = nextLong(_pData);
272 zFactor = nextLong(_pData);
273 nodecount = nextLong(_pData);
274 pnodelistx = nextLong(_pData);
275 pnodelisty = nextLong(_pData);
276 plinelist = nextLong(_pData);
277
278 nlistx = (const int32 *)(_pStart + (int)FROM_32(pnodelistx));
279 nlisty = (const int32 *)(_pStart + (int)FROM_32(pnodelisty));
280
281 if (TinselV0)
282 // Skip to the last 4 bytes of the record for the hScript value
283 _pData = pRecord + 0x62C;
284
285 hScript = nextLong(_pData);
286 }
287
288 //----------------- LOCAL GLOBAL DATA --------------------
289
290 // FIXME: Avoid non-const global vars
291
292 static int MaxPolys = MAX_POLY;
293
294 static POLYGON *Polys[MAX_POLY+1];
295
296 static POLYGON *Polygons = 0;
297
298 static SCNHANDLE pHandle = 0; // } Set at start of each scene
299 static int noofPolys = 0; // }
300
301 static POLYGON extraBlock; // Used for dynamic blocking
302
303 static int pathsOnRoute = 0;
304 static const POLYGON *RoutePaths[MAXONROUTE];
305
306 static POLYGON *RouteEnd = 0;
307
308 #ifdef DEBUG
309 int highestYet = 0;
310 #endif
311
312 // dead/alive, offsets
313 static POLY_VOLATILE volatileStuff[MAX_POLY];
314
315 //----------------- LOCAL MACROS --------------------
316
317 // The str parameter is no longer used
318 #define CHECK_HP_OR(mvar, str) assert((mvar >= 0 && mvar <= noofPolys) || mvar == MAX_POLY);
319 #define CHECK_HP(mvar, str) assert(mvar >= 0 && mvar <= noofPolys);
320
321 //-------------------- METHODS ----------------------
322
PolygonIndex(const POLYGON * pp)323 static HPOLYGON PolygonIndex(const POLYGON *pp) {
324 for (int i = 0; i <= MAX_POLY; ++i) {
325 if (Polys[i] == pp)
326 return i;
327 }
328
329 error("PolygonIndex(): polygon not found");
330 }
331
332 /**
333 * Returns TRUE if the point is within the polygon supplied.
334 *
335 * Firstly, the point must be within the smallest imaginary rectangle
336 * which encloses the polygon.
337 *
338 * Then, from each corner of the polygon, if the point is within an
339 * imaginary rectangle enclosing the clockwise-going side from that
340 * corner, the gradient of a line from the corner to the point must be
341 * less than (or more negative than) the gradient of that side:
342 *
343 * If the corners' coordinates are designated (x1, y1) and (x2, y2), and
344 * the point in question's (xt, yt), then:
345 * gradient (x1,y1)->(x2,y2) > gradient (x1,y1)->(xt,yt)
346 * (y1-y2)/(x2-x1) > (y1-yt)/(xt-x1)
347 * (y1-y2)*(xt-x1) > (y1-yt)*(x2-x1)
348 * xt(y1-y2) -x1y1 + x1y2 > -yt(x2-x1) + y1x2 - x1y1
349 * xt(y1-y2) + yt(x2-x1) > y1x2 - x1y2
350 *
351 * If the point passed one of the four 'side tests', and failed none,
352 * then it must be within the polygon. If the point was not tested, it
353 * may be within the internal rectangle not covered by the above tests.
354 *
355 * Most polygons contain an internal rectangle which does not fall into
356 * any of the above side-related tests. Such a rectangle will always
357 * have two polygon corners above it and two corners to the left of it.
358 */
IsInPolygon(int xt,int yt,HPOLYGON hp)359 bool IsInPolygon(int xt, int yt, HPOLYGON hp) {
360 const POLYGON *pp;
361 int i;
362 bool BeenTested = false;
363 int pl = 0, pa = 0;
364
365 CHECK_HP_OR(hp, "Out of range polygon handle (1)");
366 pp = Polys[hp];
367 assert(pp != NULL); // Testing whether in a NULL polygon
368
369 // Shift cursor for relative polygons
370 if (TinselV2) {
371 xt -= volatileStuff[hp].xoff;
372 yt -= volatileStuff[hp].yoff;
373 }
374
375 /* Is point within the external rectangle? */
376 if (xt < pp->pleft || xt > pp->pright || yt < pp->ptop || yt > pp->pbottom)
377 return false;
378
379 // For each corner/side
380 for (i = 0; i < 4; i++) {
381 // If within this side's 'testable' area
382 // i.e. within the width of the line in y direction of end of line
383 // or within the height of the line in x direction of end of line
384 if ((xt >= pp->lleft[i] && xt <= pp->lright[i] && ((yt > pp->cy[i]) == (pp->cy[(i+1)%4] > pp->cy[i])))
385 || (yt >= pp->ltop[i] && yt <= pp->lbottom[i] && ((xt > pp->cx[i]) == (pp->cx[(i+1)%4] > pp->cx[i])))) {
386 if (((long)xt*pp->a[i] + (long)yt*pp->b[i]) < pp->c[i])
387 return false;
388 else
389 BeenTested = true;
390 }
391 }
392
393 if (BeenTested) {
394 // New dodgy code 29/12/94
395 if (pp->polyType == BLOCK) {
396 // For each corner/side
397 for (i = 0; i < 4; i++) {
398 // Pretend the corners of blocking polys are not in the poly.
399 if (xt == pp->cx[i] && yt == pp->cy[i])
400 return false;
401 }
402 }
403 return true;
404 } else {
405 // Is point within the internal rectangle?
406 for (i = 0; i < 4; i++) {
407 if (pp->cx[i] < xt)
408 pl++;
409 if (pp->cy[i] < yt)
410 pa++;
411 }
412
413 if (pa == 2 && pl == 2)
414 return true;
415 else
416 return false;
417 }
418 }
419
420 /**
421 * Finds a polygon of the specified type containing the supplied point.
422 */
InPolygon(int xt,int yt,PTYPE type)423 HPOLYGON InPolygon(int xt, int yt, PTYPE type) {
424 for (int j = 0; j <= MAX_POLY; j++) {
425 if (Polys[j] && Polys[j]->polyType == type) {
426 if (IsInPolygon(xt, yt, j))
427 return j;
428 }
429 }
430 return NOPOLY;
431 }
432
433 /**
434 * Given a blocking polygon, current co-ordinates of an actor, and the
435 * co-ordinates of where the actor is heading, works out which corner of
436 * the blocking polygon to head around.
437 */
438
BlockingCorner(HPOLYGON hp,int * x,int * y,int tarx,int tary)439 void BlockingCorner(HPOLYGON hp, int *x, int *y, int tarx, int tary) {
440 const POLYGON *pp;
441 int i;
442 int xd, yd; // distance per axis
443 int ThisD, SmallestD = 1000;
444 int D1, D2;
445 int NearestToHere = 1000, NearestToTarget;
446 unsigned At = 10; // Corner already at
447
448 int bcx[4], bcy[4]; // Bogus corners
449
450 CHECK_HP_OR(hp, "Out of range polygon handle (2)");
451 pp = Polys[hp];
452
453 // Work out a point outside each corner
454 for (i = 0; i < 4; i++) {
455 int next, prev;
456
457 // X-direction
458 next = pp->cx[i] - pp->cx[(i+1)%4];
459 prev = pp->cx[i] - pp->cx[(i+3)%4];
460 if (next <= 0 && prev <= 0)
461 bcx[i] = pp->cx[i] - 4; // Both points to the right
462 else if (next >= 0 && prev >= 0)
463 bcx[i] = pp->cx[i] + 4; // Both points to the left
464 else
465 bcx[i] = pp->cx[i];
466
467 // Y-direction
468 next = pp->cy[i] - pp->cy[(i+1)%4];
469 prev = pp->cy[i] - pp->cy[(i+3)%4];
470 if (next <= 0 && prev <= 0)
471 bcy[i] = pp->cy[i] - 4; // Both points below
472 else if (next >= 0 && prev >= 0)
473 bcy[i] = pp->cy[i] + 4; // Both points above
474 else
475 bcy[i] = pp->cy[i];
476 }
477
478 // Find nearest corner to where we are,
479 // but not the one we're stood at.
480
481 for (i = 0; i < 4; i++) { // For 4 corners
482 // ThisD = ABS(*x - pp->cx[i]) + ABS(*y - pp->cy[i]);
483 ThisD = ABS(*x - bcx[i]) + ABS(*y - bcy[i]);
484 if (ThisD < SmallestD) {
485 // Ignore this corner if it's not in a path
486 if (InPolygon(pp->cx[i], pp->cy[i], PATH) == NOPOLY ||
487 InPolygon(bcx[i], bcy[i], PATH) == NOPOLY)
488 continue;
489
490 // Are we stood at this corner?
491 if (ThisD > 4) {
492 // No - it's the nearest we've found yet.
493 NearestToHere = i;
494 SmallestD = ThisD;
495 } else {
496 // Stood at/next to this corner
497 At = i;
498 }
499 }
500 }
501
502 // If we're not already at a corner, go to the nearest corner
503
504 if (At == 10) {
505 // Not stood at a corner
506 // assert(NearestToHere != 1000); // At blocking corner, not found near corner!
507 // Better to give up than to assert fail!
508 if (NearestToHere == 1000) {
509 // Send it to where it is now
510 // i.e. leave x and y alone
511 } else {
512 *x = bcx[NearestToHere];
513 *y = bcy[NearestToHere];
514 }
515 } else {
516 // Already at a corner. Go to an adjacent corner.
517 // First, find out which adjacent corner is nearest the target.
518 xd = ABS(tarx - pp->cx[(At + 1) % 4]);
519 yd = ABS(tary - pp->cy[(At + 1) % 4]);
520 D1 = xd + yd;
521 xd = ABS(tarx - pp->cx[(At + 3) % 4]);
522 yd = ABS(tary - pp->cy[(At + 3) % 4]);
523 D2 = xd + yd;
524 NearestToTarget = (D2 > D1) ? (At + 1) % 4 : (At + 3) % 4;
525 if (NearestToTarget == NearestToHere) {
526 *x = bcx[NearestToHere];
527 *y = bcy[NearestToHere];
528 } else {
529 // Need to decide whether it's better to go to the nearest to
530 // here and then on to the target, or to the nearest to the
531 // target and on from there.
532 xd = ABS(pp->cx[At] - pp->cx[NearestToHere]);
533 D1 = xd;
534 xd = ABS(pp->cx[NearestToHere] - tarx);
535 D1 += xd;
536
537 yd = ABS(pp->cy[At] - pp->cy[NearestToHere]);
538 D1 += yd;
539 yd = ABS(pp->cy[NearestToHere] - tary);
540 D1 += yd;
541
542 xd = ABS(pp->cx[At] - pp->cx[NearestToTarget]);
543 D2 = xd;
544 xd = ABS(pp->cx[NearestToTarget] - tarx);
545 D2 += xd;
546
547 yd = ABS(pp->cy[At] - pp->cy[NearestToTarget]);
548 D2 += yd;
549 yd = ABS(pp->cy[NearestToTarget] - tary);
550 D2 += yd;
551
552 if (D2 > D1) {
553 *x = bcx[NearestToHere];
554 *y = bcy[NearestToHere];
555 } else {
556 *x = bcx[NearestToTarget];
557 *y = bcy[NearestToTarget];
558 }
559 }
560 }
561 }
562
563
564 /**
565 * Try do drop a perpendicular to each inter-node line from the point
566 * and remember the shortest (if any).
567 * Find which node is nearest to the point.
568 * The shortest of these gives the best point in the node path.
569 */
FindBestPoint(HPOLYGON hp,int * x,int * y,int * pline)570 void FindBestPoint(HPOLYGON hp, int *x, int *y, int *pline) {
571 const POLYGON *pp;
572
573 int dropD; // length of perpendicular (i.e. distance of point from line)
574 int dropX, dropY; // (X, Y) where dropped perpendicular intersects the line
575 int d1, d2; // distance from perpendicular intersect to line's end nodes
576
577 int shortestD = 10000; // Shortest distance found
578 int nearestL = -1; // Nearest line
579 int nearestN; // Nearest Node
580
581 int h = *x; // For readability/conveniance
582 int k = *y; // - why aren't these #defines?
583
584 CHECK_HP(hp, "Out of range polygon handle (3)");
585 pp = Polys[hp];
586
587 // Pointer to polygon data
588 Poly ptp(LockMem(pHandle), pp->pIndex); // This polygon
589
590 // Look for fit of perpendicular to lines between nodes
591 for (int i = 0; i < ptp.getNodecount() - 1; i++) {
592 const LINEINFO *line = ptp.getLineinfo(i);
593
594 const int32 a = (int)FROM_32(line->a);
595 const int32 b = (int)FROM_32(line->b);
596 const int32 c = (int)FROM_32(line->c);
597
598 #if 1
599 // TODO: If the comments of the LINEINFO struct are correct, then it contains mostly
600 // duplicate data, probably in an effort to safe CPU cycles. Even on the slowest devices
601 // we support, calculating a product of two ints is not an issue.
602 // So we can just load & endian convert a,b,c, then replace stuff like
603 // (int)FROM_32(line->ab)
604 // by simply a*b, which makes it easier to understand what the code does, too.
605 // Just in case there is some bugged data, I leave this code here for verifying it.
606 // Let's leave it in for some time.
607 //
608 // One bad thing: We use sqrt to compute a square root. Might not be a good idea,
609 // speed wise. Maybe we should take Vicent's fp_sqroot. But that's a problem for later.
610
611 int32 a2 = (int)FROM_32(line->a2); ///< a squared
612 int32 b2 = (int)FROM_32(line->b2); ///< b squared
613 int32 a2pb2 = (int)FROM_32(line->a2pb2); ///< a squared + b squared
614 int32 ra2pb2 = (int)FROM_32(line->ra2pb2); ///< root(a squared + b squared)
615
616 int32 ab = (int)FROM_32(line->ab);
617 int32 ac = (int)FROM_32(line->ac);
618 int32 bc = (int)FROM_32(line->bc);
619
620 assert(a*a == a2);
621 assert(b*b == b2);
622 assert(a*b == ab);
623 assert(a*c == ac);
624 assert(b*c == bc);
625
626 assert(a2pb2 == a*a + b*b);
627 assert(ra2pb2 == (int)sqrt((float)a*a + (float)b*b));
628 #endif
629
630
631 if (a == 0 && b == 0)
632 continue; // Line is just a point!
633
634 // X position of dropped perpendicular intersection with line
635 dropX = ((b*b * h) - (a*b * k) - a*c) / (a*a + b*b);
636
637 // X distances from intersection to end nodes
638 d1 = dropX - ptp.getNodeX(i);
639 d2 = dropX - ptp.getNodeX(i+1);
640
641 // if both -ve or both +ve, no fit
642 if ((d1 < 0 && d2 < 0) || (d1 > 0 && d2 > 0))
643 continue;
644 //#if 0
645 // Y position of sidweays perpendicular intersection with line
646 dropY = ((a*a * k) - (a*b * h) - b*c) / (a*a + b*b);
647
648 // Y distances from intersection to end nodes
649 d1 = dropY - ptp.getNodeY(i);
650 d2 = dropY - ptp.getNodeY(i+1);
651
652 // if both -ve or both +ve, no fit
653 if ((d1 < 0 && d2 < 0) || (d1 > 0 && d2 > 0))
654 continue;
655 //#endif
656 dropD = ((a * h) + (b * k) + c) / (int)sqrt((float)a*a + (float)b*b);
657 dropD = ABS(dropD);
658 if (dropD < shortestD) {
659 shortestD = dropD;
660 nearestL = i;
661 }
662 }
663
664 // Distance to nearest node
665 nearestN = NearestNodeWithin(hp, h, k);
666 dropD = ABS(h - ptp.getNodeX(nearestN)) + ABS(k - ptp.getNodeY(nearestN));
667
668 // Go to a node or a point on a line
669 if (dropD < shortestD) {
670 // A node is nearest
671 *x = ptp.getNodeX(nearestN);
672 *y = ptp.getNodeY(nearestN);
673 *pline = nearestN;
674 } else {
675 assert(nearestL != -1);
676
677 // A point on a line is nearest
678 const LINEINFO *line = ptp.getLineinfo(nearestL);
679 const int32 a = (int)FROM_32(line->a);
680 const int32 b = (int)FROM_32(line->b);
681 const int32 c = (int)FROM_32(line->c);
682 dropX = ((b*b * h) - (a*b * k) - a*c) / (a*a + b*b);
683 dropY = ((a*a * k) - (a*b * h) - b*c) / (a*a + b*b);
684 *x = dropX;
685 *y = dropY;
686 *pline = nearestL;
687 }
688
689 assert(IsInPolygon(*x, *y, hp)); // Nearest point is not in polygon(!)
690 }
691
692 /**
693 * Returns TRUE if two paths are asdjacent.
694 */
IsAdjacentPath(HPOLYGON hPath1,HPOLYGON hPath2)695 bool IsAdjacentPath(HPOLYGON hPath1, HPOLYGON hPath2) {
696 const POLYGON *pp1, *pp2;
697
698 CHECK_HP(hPath1, "Out of range polygon handle (4)");
699 CHECK_HP(hPath2, "Out of range polygon handle (500)");
700
701 if (hPath1 == hPath2)
702 return true;
703
704 pp1 = Polys[hPath1];
705 pp2 = Polys[hPath2];
706
707 for (int j = 0; j < MAXADJ; j++)
708 if (pp1->adjpaths[j] == pp2)
709 return true;
710
711 return false;
712 }
713
TryPath(POLYGON * last,POLYGON * whereto,POLYGON * current)714 static const POLYGON *TryPath(POLYGON *last, POLYGON *whereto, POLYGON *current) {
715 POLYGON *x;
716
717 // For each path adjacent to this one
718 for (int j = 0; j < MAXADJ; j++) {
719 x = current->adjpaths[j]; // call the adj. path x
720 if (x == whereto) {
721 RoutePaths[pathsOnRoute++] = x;
722 return x; // Got there!
723 }
724
725 if (x == NULL)
726 break; // no more adj. paths to look at
727
728 if (x->tried)
729 continue; // don't double back
730
731 if (x == last)
732 continue; // don't double back
733
734 x->tried = true;
735 if (TryPath(current, whereto, x) != NULL) {
736 RoutePaths[pathsOnRoute++] = x;
737 assert(pathsOnRoute < MAXONROUTE);
738 return x; // Got there in this direction
739 } else
740 x->tried = false;
741 }
742
743 return NULL;
744 }
745
746
747 /**
748 * Sort out the first path to head to for the imminent leg of a walk.
749 */
PathOnTheWay(HPOLYGON from,HPOLYGON to)750 static HPOLYGON PathOnTheWay(HPOLYGON from, HPOLYGON to) {
751 // TODO: Fingolfin says: This code currently uses DFS (depth first search),
752 // in the TryPath function, to compute a path between 'from' and 'to'.
753 // However, a BFS (breadth first search) might yield more natural results,
754 // at least in cases where there are multiple possible paths.
755 // There is a small risk of regressions caused by such a change, though.
756 //
757 // Also, the overhead of computing a DFS again and again could be avoided
758 // by computing a path matrix (like we do in the SCUMM engine).
759 int i;
760
761 CHECK_HP(from, "Out of range polygon handle (501a)");
762 CHECK_HP(to, "Out of range polygon handle (501b)");
763
764 if (IsAdjacentPath(from, to))
765 return to;
766
767 for (i = 0; i < MAX_POLY; i++) { // For each polygon..
768 POLYGON *p = Polys[i];
769 if (p && p->polyType == PATH) //...if it's a path
770 p->tried = false;
771 }
772 Polys[from]->tried = true;
773 pathsOnRoute = 0;
774
775 const POLYGON *p = TryPath(Polys[from], Polys[to], Polys[from]);
776
777 if (TinselV2 && !p)
778 return NOPOLY;
779
780 assert(p != NULL); // Trying to find route between unconnected paths
781
782 // Don't go a roundabout way to an adjacent path.
783 for (i = 0; i < pathsOnRoute; i++) {
784 CHECK_HP(PolygonIndex(RoutePaths[i]), "Out of range polygon handle (502)");
785 if (IsAdjacentPath(from, PolygonIndex(RoutePaths[i])))
786 return PolygonIndex(RoutePaths[i]);
787 }
788 return PolygonIndex(p);
789 }
790
791 /**
792 * Indirect method of calling PathOnTheWay().
793 * Used to be implemented using coroutines, to put the burden of
794 * recursion onto the main stack. Since our "fake" coroutines use the
795 * same stack for everything anyway, we can do without the coroutines.
796 */
GetPathOnTheWay(HPOLYGON hFrom,HPOLYGON hTo)797 HPOLYGON GetPathOnTheWay(HPOLYGON hFrom, HPOLYGON hTo) {
798 CHECK_HP(hFrom, "Out of range polygon handle (6)");
799 CHECK_HP(hTo, "Out of range polygon handle (7)");
800
801 // Reuse already computed result
802 if (RouteEnd == Polys[hTo]) {
803 for (int i = 0; i < pathsOnRoute; i++) {
804 CHECK_HP(PolygonIndex(RoutePaths[i]), "Out of range polygon handle (503)");
805 if (IsAdjacentPath(hFrom, PolygonIndex(RoutePaths[i]))) {
806 return PolygonIndex(RoutePaths[i]);
807 }
808 }
809 }
810
811 RouteEnd = Polys[hTo];
812 return PathOnTheWay(hFrom, hTo);
813 }
814
815
816 /**
817 * Given a node path, work out which end node is nearest the given point.
818 */
NearestEndNode(HPOLYGON hPath,int x,int y)819 int NearestEndNode(HPOLYGON hPath, int x, int y) {
820 const POLYGON *pp;
821
822 int d1, d2;
823
824 CHECK_HP(hPath, "Out of range polygon handle (8)");
825 pp = Polys[hPath];
826
827 Poly ptp(LockMem(pHandle), pp->pIndex); // This polygon
828
829 const int nodecount = ptp.getNodecount() - 1;
830
831 d1 = ABS(x - ptp.getNodeX(0)) + ABS(y - ptp.getNodeY(0));
832 d2 = ABS(x - ptp.getNodeX(nodecount)) + ABS(y - ptp.getNodeY(nodecount));
833
834 return (d2 > d1) ? 0 : nodecount;
835 }
836
837
838 /**
839 * Given a start path and a destination path, find which pair of end
840 * nodes is nearest together.
841 * Return which node in the start path is part of the closest pair.
842 */
NearEndNode(HPOLYGON hSpath,HPOLYGON hDpath)843 int NearEndNode(HPOLYGON hSpath, HPOLYGON hDpath) {
844 const POLYGON *pSpath, *pDpath;
845
846 int dist, NearDist;
847 int NearNode;
848
849 CHECK_HP(hSpath, "Out of range polygon handle (9)");
850 CHECK_HP(hDpath, "Out of range polygon handle (10)");
851 pSpath = Polys[hSpath];
852 pDpath = Polys[hDpath];
853
854 uint8 *pps = LockMem(pHandle); // All polygons
855 Poly ps(pps, pSpath->pIndex); // Start polygon
856 Poly pd(pps, pDpath->pIndex); // Dest polygon
857
858 // 'top' nodes in each path
859 const int ns = ps.getNodecount() - 1;
860 const int nd = pd.getNodecount() - 1;
861
862 // start[0] to dest[0]
863 NearDist = ABS(ps.getNodeX(0) - pd.getNodeX(0)) + ABS(ps.getNodeY(0) - pd.getNodeY(0));
864 NearNode = 0;
865
866 // start[0] to dest[top]
867 dist = ABS(ps.getNodeX(0) - pd.getNodeX(nd)) + ABS(ps.getNodeY(0) - pd.getNodeY(nd));
868 if (dist < NearDist)
869 NearDist = dist;
870
871 // start[top] to dest[0]
872 dist = ABS(ps.getNodeX(ns) - pd.getNodeX(0)) + ABS(ps.getNodeY(ns) - pd.getNodeY(0));
873 if (dist < NearDist) {
874 NearDist = dist;
875 NearNode = ns;
876 }
877
878 // start[top] to dest[top]
879 dist = ABS(ps.getNodeX(ns) - pd.getNodeX(nd)) + ABS(ps.getNodeY(ns) - pd.getNodeY(nd));
880 if (dist < NearDist) {
881 NearNode = ns;
882 }
883
884 return NearNode;
885 }
886
887 /**
888 * Given a follow nodes path and a co-ordinate, finds which node in the
889 * path is nearest to the co-ordinate.
890 */
NearestNodeWithin(HPOLYGON hNpath,int x,int y)891 int NearestNodeWithin(HPOLYGON hNpath, int x, int y) {
892 int ThisDistance, SmallestDistance = 1000;
893 int NearestYet = 0; // Number of nearest node
894
895 CHECK_HP(hNpath, "Out of range polygon handle (11)");
896
897 Poly ptp(LockMem(pHandle), Polys[hNpath]->pIndex); // This polygon
898
899 const int numNodes = ptp.getNodecount(); // Number of nodes in this follow nodes path
900
901 for (int i = 0; i < numNodes; i++) {
902 ThisDistance = ABS(x - ptp.getNodeX(i)) + ABS(y - ptp.getNodeY(i));
903
904 if (ThisDistance < SmallestDistance) {
905 NearestYet = i;
906 SmallestDistance = ThisDistance;
907 }
908 }
909
910 return NearestYet;
911 }
912
913 /**
914 * Given a point and start and destination paths, find the nearest
915 * corner (if any) of the start path which is within the destination
916 * path. If there is no such corner, find the nearest corner of the
917 * destination path which falls within the source path.
918 */
NearestCorner(int * x,int * y,HPOLYGON hStartPoly,HPOLYGON hDestPoly)919 void NearestCorner(int *x, int *y, HPOLYGON hStartPoly, HPOLYGON hDestPoly) {
920 const POLYGON *psp, *pdp;
921 int j;
922 int ncorn = 0; // nearest corner
923 HPOLYGON hNpath = NOPOLY; // path containing nearest corner
924 int ThisD, SmallestD = 1000;
925
926 CHECK_HP(hStartPoly, "Out of range polygon handle (12)");
927 CHECK_HP(hDestPoly, "Out of range polygon handle (13)");
928
929 psp = Polys[hStartPoly];
930 pdp = Polys[hDestPoly];
931
932 // Nearest corner of start path in destination path.
933
934 for (j = 0; j < 4; j++) {
935 if (IsInPolygon(psp->cx[j], psp->cy[j], hDestPoly)) {
936 ThisD = ABS(*x - psp->cx[j]) + ABS(*y - psp->cy[j]);
937 if (ThisD < SmallestD) {
938 hNpath = hStartPoly;
939 ncorn = j;
940 // Try to ignore it if virtually stood on it
941 if (ThisD > 4)
942 SmallestD = ThisD;
943 }
944 }
945 }
946 if (SmallestD == 1000) {
947 // Nearest corner of destination path in start path.
948 for (j = 0; j < 4; j++) {
949 if (IsInPolygon(pdp->cx[j], pdp->cy[j], hStartPoly)) {
950 ThisD = ABS(*x - pdp->cx[j]) + ABS(*y - pdp->cy[j]);
951 if (ThisD < SmallestD) {
952 hNpath = hDestPoly;
953 ncorn = j;
954 // Try to ignore it if virtually stood on it
955 if (ThisD > 4)
956 SmallestD = ThisD;
957 }
958 }
959 }
960 }
961
962 if (hNpath != NOPOLY) {
963 *x = Polys[hNpath]->cx[ncorn];
964 *y = Polys[hNpath]->cy[ncorn];
965 } else
966 error("NearestCorner() failure");
967 }
968
IsPolyCorner(HPOLYGON hPath,int x,int y)969 bool IsPolyCorner(HPOLYGON hPath, int x, int y) {
970 CHECK_HP(hPath, "Out of range polygon handle (37)");
971
972 for (int i = 0; i < 4; i++) {
973 if (Polys[hPath]->cx[i] == x && Polys[hPath]->cy[i] == y)
974 return true;
975 }
976 return false;
977 }
978
979 /**
980 * Given a path polygon and a Y co-ordinate, return a scale value.
981 */
GetScale(HPOLYGON hPath,int y)982 int GetScale(HPOLYGON hPath, int y) {
983 int zones; // Number of different scales
984 int zlen; // Depth of each scale zone
985 int scale;
986 int top;
987
988 // To try and fix some unknown potential bug
989 if (hPath == NOPOLY)
990 return SCALE_LARGE;
991
992 CHECK_HP(hPath, "Out of range polygon handle (14)");
993
994 Poly ptp(LockMem(pHandle), Polys[hPath]->pIndex);
995
996 // Path is of a constant scale?
997 if (FROM_32(ptp.scale2) == 0)
998 return FROM_32(ptp.scale1);
999
1000 assert(FROM_32(ptp.scale1) >= FROM_32(ptp.scale2));
1001
1002 zones = FROM_32(ptp.scale1) - FROM_32(ptp.scale2) + 1;
1003 zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones;
1004
1005 scale = FROM_32(ptp.scale1);
1006 top = Polys[hPath]->ptop;
1007
1008 do {
1009 top += zlen;
1010 if (y < top)
1011 return scale;
1012 } while (--scale);
1013
1014 return FROM_32(ptp.scale2);
1015 }
1016
1017 /**
1018 * Given a path polygon and a Y co-ordinate, return a brightness value.
1019 */
1020
GetBrightness(HPOLYGON hPath,int y)1021 int GetBrightness(HPOLYGON hPath, int y) {
1022 int zones; // Number of different brightnesses
1023 int zlen; // Depth of each brightness zone
1024 int brightness;
1025 int top;
1026
1027 // To try and fix some unknown potential bug
1028 if (hPath == NOPOLY)
1029 return 10;
1030
1031 CHECK_HP(hPath, "Out of range polygon handle (38)");
1032
1033 Poly ptp(LockMem(pHandle), Polys[hPath]->pIndex);
1034
1035 // Path is of a constant brightness?
1036 if (FROM_32(ptp.bright1) == FROM_32(ptp.bright2))
1037 return FROM_32(ptp.bright1);
1038
1039 assert(FROM_32(ptp.bright1) >= FROM_32(ptp.bright2));
1040
1041 zones = FROM_32(ptp.bright1) - FROM_32(ptp.bright2) + 1;
1042 zlen = (Polys[hPath]->pbottom - Polys[hPath]->ptop) / zones;
1043
1044 brightness = FROM_32(ptp.bright1);
1045 top = Polys[hPath]->ptop;
1046
1047 do {
1048 top += zlen;
1049 if (y < top)
1050 return brightness;
1051 } while (--brightness);
1052
1053 return FROM_32(ptp.bright2);
1054 }
1055
1056
1057 /**
1058 * Give the co-ordinates of a node in a node path.
1059 */
getNpathNode(HPOLYGON hNpath,int node,int * px,int * py)1060 void getNpathNode(HPOLYGON hNpath, int node, int *px, int *py) {
1061 CHECK_HP(hNpath, "Out of range polygon handle (15)");
1062 assert(Polys[hNpath] != NULL && Polys[hNpath]->polyType == PATH && Polys[hNpath]->subtype == NODE); // must be given a node path!
1063
1064 Poly ptp(LockMem(pHandle), Polys[hNpath]->pIndex); // This polygon
1065
1066 // Might have just walked to the node from above.
1067 if (node == ptp.getNodecount())
1068 node -= 1;
1069
1070 *px = ptp.getNodeX(node);
1071 *py = ptp.getNodeY(node);
1072 }
1073
1074 /**
1075 * Get compiled tag text handle and tag co-ordinates of a tag polygon.
1076 */
GetTagTag(HPOLYGON hp,SCNHANDLE * hTagText,int * tagx,int * tagy)1077 void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagx, int *tagy) {
1078 CHECK_HP(hp, "Out of range polygon handle (16)");
1079
1080 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1081
1082 *tagx = (int)FROM_32(ptp.tagx) + (TinselV2 ? volatileStuff[hp].xoff : 0);
1083 *tagy = (int)FROM_32(ptp.tagy) + (TinselV2 ? volatileStuff[hp].yoff : 0);
1084 *hTagText = FROM_32(ptp.hTagtext);
1085 }
1086
1087 /**
1088 * Get polygon's film reel handle.
1089 */
GetPolyFilm(HPOLYGON hp)1090 SCNHANDLE GetPolyFilm(HPOLYGON hp) {
1091 CHECK_HP(hp, "Out of range polygon handle (17)");
1092
1093 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1094
1095 return FROM_32(ptp.hFilm);
1096 }
1097
1098 /**
1099 * Get handle to polygon's glitter code.
1100 */
GetPolyScript(HPOLYGON hp)1101 SCNHANDLE GetPolyScript(HPOLYGON hp) {
1102 CHECK_HP(hp, "Out of range polygon handle (19)");
1103
1104 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1105
1106 return FROM_32(ptp.hScript);
1107 }
1108
GetPolyReelType(HPOLYGON hp)1109 REEL GetPolyReelType(HPOLYGON hp) {
1110 // To try and fix some unknown potential bug (toyshop entrance)
1111 if (hp == NOPOLY)
1112 return REEL_ALL;
1113
1114 CHECK_HP(hp, "Out of range polygon handle (20)");
1115
1116 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1117
1118 return (REEL)FROM_32(ptp.reel);
1119 }
1120
GetPolyZfactor(HPOLYGON hp)1121 int32 GetPolyZfactor(HPOLYGON hp) {
1122 CHECK_HP(hp, "Out of range polygon handle (21)");
1123 assert(Polys[hp] != NULL);
1124
1125 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1126
1127 return (int)FROM_32(ptp.zFactor);
1128 }
1129
numNodes(HPOLYGON hp)1130 int numNodes(HPOLYGON hp) {
1131 CHECK_HP(hp, "Out of range polygon handle (22)");
1132 assert(Polys[hp] != NULL);
1133
1134 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1135
1136 return ptp.getNodecount();
1137 }
1138
1139 // *************************************************************************
1140 //
1141 // Code concerned with killing and reviving TAG and EXIT polygons.
1142 // And code to enable this information to be saved and restored.
1143 //
1144 // *************************************************************************
1145
1146 struct TAGSTATE {
1147 int tid;
1148 bool enabled;
1149 };
1150
1151 #define MAX_SCENES 256
1152 #define MAX_TAGS 2048
1153 #define MAX_EXITS 512
1154
1155 static struct {
1156 SCNHANDLE sid;
1157 int nooftags;
1158 int offset;
1159 } SceneTags[MAX_SCENES], SceneExits[MAX_SCENES];
1160
1161 static TAGSTATE TagStates[MAX_TAGS];
1162 static TAGSTATE ExitStates[MAX_EXITS];
1163
1164 static int nextfreeT = 0, numScenesT = 0;
1165 static int nextfreeE = 0, numScenesE = 0;
1166
1167 static int currentTScene = 0;
1168 static int currentEScene = 0;
1169
1170 bool deadPolys[MAX_POLY]; // Currently just for dead blocks
1171
RebootDeadTags()1172 void RebootDeadTags() {
1173 nextfreeT = numScenesT = 0;
1174 nextfreeE = numScenesE = 0;
1175
1176 memset(SceneTags, 0, sizeof(SceneTags));
1177 memset(SceneExits, 0, sizeof(SceneExits));
1178 memset(TagStates, 0, sizeof(TagStates));
1179 memset(ExitStates, 0, sizeof(ExitStates));
1180 memset(deadPolys, 0, sizeof(deadPolys));
1181 }
1182
1183 /**
1184 * (Un)serialize the dead tag and exit data for save/restore game.
1185 */
syncPolyInfo(Common::Serializer & s)1186 void syncPolyInfo(Common::Serializer &s) {
1187 int i;
1188
1189 for (i = 0; i < MAX_SCENES; i++) {
1190 s.syncAsUint32LE(SceneTags[i].sid);
1191 s.syncAsSint32LE(SceneTags[i].nooftags);
1192 s.syncAsSint32LE(SceneTags[i].offset);
1193 }
1194
1195 for (i = 0; i < MAX_SCENES; i++) {
1196 s.syncAsUint32LE(SceneExits[i].sid);
1197 s.syncAsSint32LE(SceneExits[i].nooftags);
1198 s.syncAsSint32LE(SceneExits[i].offset);
1199 }
1200
1201 for (i = 0; i < MAX_TAGS; i++) {
1202 s.syncAsUint32LE(TagStates[i].tid);
1203 s.syncAsSint32LE(TagStates[i].enabled);
1204 }
1205
1206 for (i = 0; i < MAX_EXITS; i++) {
1207 s.syncAsUint32LE(ExitStates[i].tid);
1208 s.syncAsSint32LE(ExitStates[i].enabled);
1209 }
1210
1211 s.syncAsSint32LE(nextfreeT);
1212 s.syncAsSint32LE(numScenesT);
1213 s.syncAsSint32LE(nextfreeE);
1214 s.syncAsSint32LE(numScenesE);
1215 }
1216
1217 /**
1218 * This is all totally different to the way the rest of the way polygon
1219 * data is stored and restored, more specifically, different to how dead
1220 * tags and exits are handled, because of the piecemeal design-by-just-
1221 * thought-of-this approach employed.
1222 */
1223
SaveDeadPolys(bool * sdp)1224 void SaveDeadPolys(bool *sdp) {
1225 assert(!TinselV2);
1226 memcpy(sdp, deadPolys, MAX_POLY*sizeof(bool));
1227 }
1228
RestoreDeadPolys(bool * sdp)1229 void RestoreDeadPolys(bool *sdp) {
1230 assert(!TinselV2);
1231 memcpy(deadPolys, sdp, MAX_POLY*sizeof(bool));
1232 }
1233
SavePolygonStuff(POLY_VOLATILE * sps)1234 void SavePolygonStuff(POLY_VOLATILE *sps) {
1235 assert(TinselV2);
1236 memcpy(sps, volatileStuff, MAX_POLY*sizeof(POLY_VOLATILE));
1237 }
1238
RestorePolygonStuff(POLY_VOLATILE * sps)1239 void RestorePolygonStuff(POLY_VOLATILE *sps) {
1240 assert(TinselV2);
1241 memcpy(volatileStuff, sps, MAX_POLY*sizeof(POLY_VOLATILE));
1242 }
1243
1244
1245 /**
1246 * Scan for a given polygon
1247 */
FindPolygon(PTYPE type,int id)1248 static HPOLYGON FindPolygon(PTYPE type, int id) {
1249
1250 for (int i = 0; i <= MAX_POLY; i++) {
1251 if (Polys[i] && Polys[i]->polyType == type && Polys[i]->polyID == id) {
1252 // Found it
1253 return i;
1254 }
1255 }
1256
1257 // Not found
1258 return NOPOLY;
1259 }
1260
FirstPathPoly()1261 HPOLYGON FirstPathPoly() {
1262 for (int i = 0; i < noofPolys; i++) {
1263 if (Polys[i]->polyType == PATH)
1264 return i;
1265 }
1266 error("FirstPathPoly() - no PATH polygons");
1267 return NOPOLY; // for compilers that don't support NORETURN
1268 }
1269
GetPolyHandle(int i)1270 HPOLYGON GetPolyHandle(int i) {
1271 assert(i >= 0 && i <= MAX_POLY);
1272
1273 return (Polys[i] != NULL) ? i : NOPOLY;
1274 }
1275
1276 // **************************************************************************
1277 //
1278 // Code called to initialize or wrap up a scene:
1279 //
1280 // **************************************************************************
1281
1282 /**
1283 * Called at the start of a scene, when all polygons have been
1284 * initialized, to work out which paths are adjacent to which.
1285 */
DistinctCorners(HPOLYGON hp1,HPOLYGON hp2)1286 static int DistinctCorners(HPOLYGON hp1, HPOLYGON hp2) {
1287 const POLYGON *pp1, *pp2;
1288 int i, j;
1289 int retval = 0;
1290
1291 CHECK_HP(hp1, "Out of range polygon handle (23)");
1292 CHECK_HP(hp2, "Out of range polygon handle (24)");
1293 pp1 = Polys[hp1];
1294 pp2 = Polys[hp2];
1295
1296 // Work out (how many of p1's corners is in p2) + (how many of p2's corners is in p1)
1297 for (i = 0; i < 4; i++) {
1298 if (IsInPolygon(pp1->cx[i], pp1->cy[i], hp2))
1299 retval++;
1300 if (IsInPolygon(pp2->cx[i], pp2->cy[i], hp1))
1301 retval++;
1302 }
1303
1304 // Common corners only count once
1305 for (i = 0; i < 4; i++) {
1306 for (j = 0; j < 4; j++) {
1307 if (pp1->cx[i] == pp2->cx[j] && pp1->cy[i] == pp2->cy[j])
1308 retval--;
1309 }
1310 }
1311 return retval;
1312 }
1313
1314 /**
1315 * Returns true if the two paths are on the same level
1316 */
MatchingLevels(PPOLYGON p1,PPOLYGON p2)1317 static bool MatchingLevels(PPOLYGON p1, PPOLYGON p2) {
1318 byte *pps = LockMem(pHandle); // All polygons
1319 Poly pp1(pps, p1->pIndex); // This polygon 1
1320 Poly pp2(pps, p2->pIndex); // This polygon 2
1321
1322 assert((int32)FROM_32(pp1.level1) <= (int32)FROM_32(pp1.level2));
1323 assert((int32)FROM_32(pp2.level1) <= (int32)FROM_32(pp2.level2));
1324
1325 for (int pl = (int32)FROM_32(pp1.level1); pl <= (int32)FROM_32(pp1.level2); pl++) {
1326 if (pl >= (int32)FROM_32(pp2.level1) && pl <= (int32)FROM_32(pp2.level2))
1327 return true;
1328 }
1329
1330 return false;
1331 }
1332
SetPathAdjacencies()1333 static void SetPathAdjacencies() {
1334 POLYGON *p1, *p2; // Polygon pointers
1335 int i1, i2;
1336
1337 // Reset them all
1338 for (i1 = 0; i1 < noofPolys; i1++)
1339 memset(Polys[i1]->adjpaths, 0, MAXADJ * sizeof(PPOLYGON));
1340
1341 // For each polygon..
1342 for (i1 = 0; i1 < MAX_POLY-1; i1++) {
1343 // Get polygon, but only carry on if it's a path
1344 p1 = Polys[i1];
1345 if (!p1 || p1->polyType != PATH)
1346 continue;
1347
1348 // For each subsequent polygon..
1349 for (i2 = i1 + 1; i2 < MAX_POLY; i2++) {
1350 // Get polygon, but only carry on if it's a path
1351 p2 = Polys[i2];
1352 if (!p2 || p2->polyType != PATH)
1353 continue;
1354
1355 // Must be on the same level
1356 if (TinselV2 && !MatchingLevels(p1, p2))
1357 continue;
1358
1359 int j = DistinctCorners(i1, i2);
1360
1361 if (j >= 2) {
1362 // Paths are adjacent
1363 for (j = 0; j < MAXADJ; j++)
1364 if (p1->adjpaths[j] == NULL) {
1365 p1->adjpaths[j] = p2;
1366 break;
1367 }
1368 #ifdef DEBUG
1369 if (j > highestYet)
1370 highestYet = j;
1371 #endif
1372 assert(j < MAXADJ); // Number of adjacent paths limit
1373 for (j = 0; j < MAXADJ; j++) {
1374 if (p2->adjpaths[j] == NULL) {
1375 p2->adjpaths[j] = p1;
1376 break;
1377 }
1378 }
1379 #ifdef DEBUG
1380 if (j > highestYet)
1381 highestYet = j;
1382 #endif
1383 assert(j < MAXADJ); // Number of adjacent paths limit
1384 }
1385 }
1386 }
1387 }
1388
1389 /**
1390 * Ensure NPATH nodes are not inside another PATH/NPATH polygon.
1391 * Only bother with end nodes for now.
1392 */
1393 #ifdef DEBUG
CheckNPathIntegrity()1394 void CheckNPathIntegrity() {
1395 uint8 *pps; // Compiled polygon data
1396 const POLYGON *rp; // Run-time polygon structure
1397 HPOLYGON hp;
1398 int i, j; // Loop counters
1399 int n; // Last node in current path
1400
1401 pps = LockMem(pHandle); // All polygons
1402
1403 for (i = 0; i < MAX_POLY; i++) { // For each polygon..
1404 rp = Polys[i];
1405 if (rp && rp->polyType == PATH && rp->subtype == NODE) { //...if it's a node path
1406 // Get compiled polygon structure
1407 const Poly cp(pps, rp->pIndex); // This polygon
1408
1409 n = cp.getNodecount() - 1; // Last node
1410 assert(n >= 1); // Node paths must have at least 2 nodes
1411
1412 hp = PolygonIndex(rp);
1413 for (j = 0; j <= n; j++) {
1414 if (!IsInPolygon(cp.getNodeX(j), cp.getNodeY(j), hp)) {
1415 sprintf(TextBufferAddr(), "Node (%d, %d) is not in its own path (starting (%d, %d))",
1416 cp.getNodeX(j), cp.getNodeY(j), rp->cx[0], rp->cy[0]);
1417 error(TextBufferAddr());
1418 }
1419 }
1420
1421 // Check end nodes are not in adjacent path
1422 for (j = 0; j < MAXADJ; j++) { // For each adjacent path
1423 if (rp->adjpaths[j] == NULL)
1424 break;
1425
1426 if (IsInPolygon(cp.getNodeX(0), cp.getNodeY(0), PolygonIndex(rp->adjpaths[j]))) {
1427 sprintf(TextBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
1428 cp.getNodeX(0), cp.getNodeY(0), rp->adjpaths[j]->cx[0], rp->adjpaths[j]->cy[0]);
1429 error(TextBufferAddr());
1430 }
1431 if (IsInPolygon(cp.getNodeX(n), cp.getNodeY(n), PolygonIndex(rp->adjpaths[j]))) {
1432 sprintf(TextBufferAddr(), "Node (%d, %d) is in another path (starting (%d, %d))",
1433 cp.getNodeX(n), cp.getNodeY(n), rp->adjpaths[j]->cx[0], rp->adjpaths[j]->cy[0]);
1434 error(TextBufferAddr());
1435 }
1436 }
1437 }
1438 }
1439 }
1440 #endif
1441
1442 /**
1443 * Called at the start of a scene, nobbles TAG polygons which should be dead.
1444 */
SetExBlocks()1445 static void SetExBlocks() {
1446 for (int i = 0; i < MAX_POLY; i++) {
1447 if (deadPolys[i]) {
1448 if (Polys[i] && Polys[i]->polyType == BLOCK)
1449 Polys[i]->polyType = EX_BLOCK;
1450 #ifdef DEBUG
1451 else
1452 error("Impossible message");
1453 #endif
1454 }
1455 }
1456 }
1457
1458 /**
1459 * Called at the start of a scene, nobbles TAG polygons which should be dead.
1460 */
SetExTags(SCNHANDLE ph)1461 static void SetExTags(SCNHANDLE ph) {
1462 int i, j;
1463 TAGSTATE *pts;
1464
1465 for (i = 0; i < numScenesT; i++) {
1466 if (SceneTags[i].sid == ph) {
1467 currentTScene = i;
1468
1469 pts = &TagStates[SceneTags[i].offset];
1470 for (j = 0; j < SceneTags[i].nooftags; j++, pts++) {
1471 if (!pts->enabled)
1472 DisableTag(Common::nullContext, pts->tid);
1473 }
1474 return;
1475 }
1476 }
1477
1478 i = numScenesT++;
1479 currentTScene = i;
1480 assert(numScenesT < MAX_SCENES); // Dead tag remembering: scene limit
1481
1482 SceneTags[i].sid = ph;
1483 SceneTags[i].offset = nextfreeT;
1484 SceneTags[i].nooftags = 0;
1485
1486 for (j = 0; j < MAX_POLY; j++) {
1487 if (Polys[j] && Polys[j]->polyType == TAG) {
1488 TagStates[nextfreeT].tid = Polys[j]->polyID;
1489 TagStates[nextfreeT].enabled = true;
1490 nextfreeT++;
1491 assert(nextfreeT < MAX_TAGS); // Dead tag remembering: tag limit
1492 SceneTags[i].nooftags++;
1493 }
1494 }
1495 }
1496
1497 /**
1498 * Called at the start of a scene, nobbles EXIT polygons which should be dead.
1499 */
SetExExits(SCNHANDLE ph)1500 static void SetExExits(SCNHANDLE ph) {
1501 TAGSTATE *pts;
1502 int i, j;
1503
1504 for (i = 0; i < numScenesE; i++) {
1505 if (SceneExits[i].sid == ph) {
1506 currentEScene = i;
1507
1508 pts = &ExitStates[SceneExits[i].offset];
1509 for (j = 0; j < SceneExits[i].nooftags; j++, pts++) {
1510 if (!pts->enabled)
1511 DisableExit(pts->tid);
1512 }
1513 return;
1514 }
1515 }
1516
1517 i = numScenesE++;
1518 currentEScene = i;
1519 assert(numScenesE < MAX_SCENES); // Dead exit remembering: scene limit
1520
1521 SceneExits[i].sid = ph;
1522 SceneExits[i].offset = nextfreeE;
1523 SceneExits[i].nooftags = 0;
1524
1525 for (j = 0; j < MAX_POLY; j++) {
1526 if (Polys[j] && Polys[j]->polyType == EXIT) {
1527 ExitStates[nextfreeE].tid = Polys[j]->polyID;
1528 ExitStates[nextfreeE].enabled = true;
1529 nextfreeE++;
1530 assert(nextfreeE < MAX_EXITS); // Dead exit remembering: exit limit
1531 SceneExits[i].nooftags++;
1532 }
1533 }
1534 }
1535
1536 /**
1537 * Works out some fixed numbers for a polygon.
1538 */
FiddlyBit(POLYGON * p)1539 static void FiddlyBit(POLYGON *p) {
1540 int t1, t2; // General purpose temp. variables
1541
1542 // Enclosing external rectangle
1543 t1 = MAX(p->cx[0], p->cx[1]);
1544 t2 = MAX(p->cx[2], p->cx[3]);
1545 p->pright = MAX(t1, t2);
1546
1547 t1 = MIN(p->cx[0], p->cx[1]);
1548 t2 = MIN(p->cx[2], p->cx[3]);
1549 p->pleft = MIN(t1, t2);
1550
1551 t1 = MAX(p->cy[0], p->cy[1]);
1552 t2 = MAX(p->cy[2], p->cy[3]);
1553 p->pbottom = MAX(t1, t2);
1554
1555 t1 = MIN(p->cy[0], p->cy[1]);
1556 t2 = MIN(p->cy[2], p->cy[3]);
1557 p->ptop = MIN(t1, t2);
1558
1559 // Rectangles enclosing each side and each side's magic numbers
1560 for (t1 = 0; t1 < 4; t1++) {
1561 p->lright[t1] = MAX(p->cx[t1], p->cx[(t1+1)%4]);
1562 p->lleft[t1] = MIN(p->cx[t1], p->cx[(t1+1)%4]);
1563
1564 p->ltop[t1] = MIN(p->cy[t1], p->cy[(t1+1)%4]);
1565 p->lbottom[t1] = MAX(p->cy[t1], p->cy[(t1+1)%4]);
1566
1567 p->a[t1] = p->cy[t1] - p->cy[(t1+1)%4];
1568 p->b[t1] = p->cx[(t1+1)%4] - p->cx[t1];
1569 p->c[t1] = (long)p->cy[t1]*p->cx[(t1+1)%4] - (long)p->cx[t1]*p->cy[(t1+1)%4];
1570 }
1571 }
1572
1573 /**
1574 * Allocate a POLYGON structure and reset it to default values
1575 */
GetPolyEntry()1576 static PPOLYGON GetPolyEntry() {
1577 int i; // Loop counter
1578 PPOLYGON p;
1579
1580 for (i = 0; i < MaxPolys; i++) {
1581 if (!Polys[i]) {
1582 p = Polys[i] = &Polygons[i];
1583
1584 // What the hell, just clear it all out - it's safer
1585 memset(p, 0, sizeof(POLYGON));
1586
1587 return p;
1588 }
1589 }
1590
1591 error("Exceeded MaxPolys");
1592 }
1593
1594 /**
1595 * Variation of GetPolyEntry from Tinsel 1 that splits up getting a new
1596 * polygon structure from initializing it
1597 */
CommonInits(PTYPE polyType,int pno,const Poly & ptp,bool bRestart)1598 static PPOLYGON CommonInits(PTYPE polyType, int pno, const Poly &ptp, bool bRestart) {
1599 int i;
1600 HPOLYGON hp;
1601 PPOLYGON p = GetPolyEntry(); // Obtain a slot
1602
1603 p->polyType = polyType; // Polygon type
1604 p->pIndex = pno;
1605
1606 for (i = 0; i < 4; i++) { // Polygon definition
1607 p->cx[i] = (short)FROM_32(ptp.x[i]);
1608 p->cy[i] = (short)FROM_32(ptp.y[i]);
1609 }
1610
1611 if (!bRestart) {
1612 hp = PolygonIndex(p);
1613 volatileStuff[hp].xoff = (short)FROM_32(ptp.xoff);
1614 volatileStuff[hp].yoff = (short)FROM_32(ptp.yoff);
1615 }
1616
1617 p->polyID = FROM_32(ptp.id); // Identifier
1618
1619 FiddlyBit(p);
1620
1621 return p;
1622 }
1623
1624 /**
1625 * Calculate a point approximating to the center of a polygon.
1626 * Not very sophisticated.
1627 */
PseudoCenter(POLYGON * p)1628 static void PseudoCenter(POLYGON *p) {
1629 p->pcenterx = (p->cx[0] + p->cx[1] + p->cx[2] + p->cx[3])/4;
1630 p->pcentery = (p->cy[0] + p->cy[1] + p->cy[2] + p->cy[3])/4;
1631
1632 if (!IsInPolygon(p->pcenterx, p->pcentery, PolygonIndex(p))) {
1633 int i, top = 0, bot = 0;
1634
1635 for (i = p->ptop; i <= p->pbottom; i++) {
1636 if (IsInPolygon(p->pcenterx, i, PolygonIndex(p))) {
1637 top = i;
1638 break;
1639 }
1640 }
1641 for (i = p->pbottom; i >= p->ptop; i--) {
1642 if (IsInPolygon(p->pcenterx, i, PolygonIndex(p))) {
1643 bot = i;
1644 break;
1645 }
1646 }
1647 p->pcenterx = (top+bot)/2;
1648 }
1649 #ifdef DEBUG
1650 // assert(IsInPolygon(p->pcenterx, p->pcentery, PolygonIndex(p))); // Pseudo-center is not in path
1651 if (!IsInPolygon(p->pcenterx, p->pcentery, PolygonIndex(p))) {
1652 sprintf(TextBufferAddr(), "Pseudo-center is not in path (starting (%d, %d)) - polygon reversed?",
1653 p->cx[0], p->cy[0]);
1654 error(TextBufferAddr());
1655 }
1656 #endif
1657 }
1658
1659 /**
1660 * Initialize an EXIT polygon.
1661 */
InitExit(const Poly & ptp,int pno,bool bRestart)1662 static void InitExit(const Poly &ptp, int pno, bool bRestart) {
1663 CommonInits(EXIT, pno, ptp, bRestart);
1664 }
1665
1666 /**
1667 * Initialize a PATH or NPATH polygon.
1668 */
InitPath(const Poly & ptp,bool NodePath,int pno,bool bRestart)1669 static void InitPath(const Poly &ptp, bool NodePath, int pno, bool bRestart) {
1670 PPOLYGON p = CommonInits(PATH, pno, ptp, bRestart);
1671
1672 p->subtype = NodePath ? NODE : NORMAL;
1673
1674 PseudoCenter(p);
1675 }
1676
1677
1678 /**
1679 * Initialize a BLOCKING polygon.
1680 */
InitBlock(const Poly & ptp,int pno,bool bRestart)1681 static void InitBlock(const Poly &ptp, int pno, bool bRestart) {
1682 CommonInits(BLOCK, pno, ptp, bRestart);
1683 }
1684
1685 /**
1686 * Initialize an extra BLOCKING polygon related to a moving actor.
1687 * The width of the polygon depends on the width of the actor which is
1688 * trying to walk through the actor you first thought of.
1689 * This is for dynamic blocking.
1690 */
InitExtraBlock(PMOVER ca,PMOVER ta)1691 HPOLYGON InitExtraBlock(PMOVER ca, PMOVER ta) {
1692 int caX, caY; // Calling actor co-ords
1693 int taX, taY; // Test actor co-ords
1694 int left, right;
1695
1696 GetMoverPosition(ca, &caX, &caY); // Calling actor co-ords
1697 GetMoverPosition(ta, &taX, &taY); // Test actor co-ords
1698
1699 left = GetMoverLeft(ta) - (GetMoverRight(ca) - caX);
1700 right = GetMoverRight(ta) + (caX - GetMoverLeft(ca));
1701
1702 memset(&extraBlock, 0, sizeof(extraBlock));
1703
1704 // The 3s on the y co-ordinates used to be 10s
1705 extraBlock.cx[0] = (short)(left - 2);
1706 extraBlock.cy[0] = (short)(taY - 3);
1707 extraBlock.cx[1] = (short)(right + 2);
1708 extraBlock.cy[1] = (short)(taY - 3);
1709 extraBlock.cx[2] = (short)(right + 2);
1710 extraBlock.cy[2] = (short)(taY + 3);
1711 extraBlock.cx[3] = (short)(left - 2);
1712 extraBlock.cy[3] = (short)(taY + 3);
1713
1714 FiddlyBit(&extraBlock); // Is this necessary?
1715
1716 Polys[MAX_POLY] = &extraBlock;
1717 return MAX_POLY;
1718 }
1719
1720 /**
1721 * Initialize an EFFECT polygon.
1722 */
InitEffect(const Poly & ptp,int pno,bool bRestart)1723 static void InitEffect(const Poly &ptp, int pno, bool bRestart) {
1724 CommonInits(EFFECT, pno, ptp, bRestart);
1725 }
1726
1727
1728 /**
1729 * Initialize a REFER polygon.
1730 */
InitRefer(const Poly & ptp,int pno,bool bRestart)1731 static void InitRefer(const Poly &ptp, int pno, bool bRestart) {
1732 PPOLYGON p = CommonInits(REFER, pno, ptp, bRestart);
1733
1734 p->subtype = FROM_32(ptp.reftype); // Refer type
1735 }
1736
1737
1738 /**
1739 * Initialize a TAG polygon.
1740 */
InitTag(const Poly & ptp,int pno,bool bRestart)1741 static void InitTag(const Poly &ptp, int pno, bool bRestart) {
1742 CommonInits(TAG, pno, ptp, bRestart);
1743 }
1744
1745 /**
1746 * Called at the restart of a scene, nobbles polygons which are dead.
1747 */
KillDeadPolygons()1748 static void KillDeadPolygons() {
1749 int i;
1750
1751 for (i = 0; i < MAX_POLY; i++) {
1752 if (volatileStuff[i].bDead) {
1753 assert(Polys[i]);
1754
1755 switch (Polys[i]->polyType) {
1756 case BLOCK:
1757 Polys[i]->polyType = EX_BLOCK;
1758 break;
1759
1760 case EFFECT:
1761 Polys[i]->polyType = EX_EFFECT;
1762 break;
1763
1764 case REFER:
1765 Polys[i]->polyType = EX_REFER;
1766 break;
1767
1768 case PATH:
1769 Polys[i]->polyType = EX_PATH;
1770 break;
1771
1772 case TAG:
1773 Polys[i]->polyType = EX_TAG;
1774 break;
1775
1776 default:
1777 error("Impossible message");
1778 }
1779 }
1780 }
1781 }
1782
1783 /**
1784 * Called at the start of a scene to initialize the polys in that scene.
1785 */
InitPolygons(SCNHANDLE ph,int numPoly,bool bRestart)1786 void InitPolygons(SCNHANDLE ph, int numPoly, bool bRestart) {
1787 pHandle = ph;
1788 noofPolys = numPoly;
1789
1790 if (Polygons == NULL) {
1791 // first time - allocate memory for process list
1792 Polygons = (POLYGON *)calloc(MaxPolys, sizeof(POLYGON));
1793
1794 // make sure memory allocated
1795 if (Polygons == NULL) {
1796 error("Cannot allocate memory for polygon data");
1797 }
1798 }
1799
1800 if (numPoly == 0)
1801 return;
1802
1803 for (int i = 0; i < noofPolys; i++) {
1804 if (Polys[i]) {
1805 Polys[i]->pointState = PS_NOT_POINTING;
1806 Polys[i] = NULL;
1807 }
1808 }
1809
1810 memset(RoutePaths, 0, sizeof(RoutePaths));
1811
1812 if (!bRestart) {
1813 if (TinselV2)
1814 memset(volatileStuff, 0, sizeof(volatileStuff));
1815 else
1816 memset(deadPolys, 0, sizeof(deadPolys));
1817 }
1818
1819 if (numPoly > 0) {
1820 Poly ptp(LockMem(ph));
1821
1822 for (int i = 0; i < numPoly; ++i, ++ptp) {
1823 switch (ptp.getType()) {
1824 case POLY_PATH:
1825 InitPath(ptp, false, i, bRestart);
1826 break;
1827
1828 case POLY_NPATH:
1829 InitPath(ptp, true, i, bRestart);
1830 break;
1831
1832 case POLY_BLOCK:
1833 InitBlock(ptp, i, bRestart);
1834 break;
1835
1836 case POLY_REFER:
1837 InitRefer(ptp, i, bRestart);
1838 break;
1839
1840 case POLY_EFFECT:
1841 InitEffect(ptp, i, bRestart);
1842 break;
1843
1844 case POLY_EXIT:
1845 InitExit(ptp, i, bRestart);
1846 break;
1847
1848 case POLY_TAG:
1849 InitTag(ptp, i, bRestart);
1850 break;
1851
1852 default:
1853 error("Unknown polygon type");
1854 }
1855 }
1856 }
1857
1858 if (!TinselV2) {
1859 SetPathAdjacencies(); // Paths need to know the facts
1860 #ifdef DEBUG
1861 CheckNPathIntegrity();
1862 #endif
1863
1864 SetExTags(ph); // Some tags may have been killed
1865 SetExExits(ph); // Some exits may have been killed
1866
1867 if (bRestart)
1868 SetExBlocks(); // Some blocks may have been killed
1869 } else {
1870 if (bRestart) {
1871 // Some may have been killed if this is a restore
1872 KillDeadPolygons();
1873 } else {
1874 for (int i = numPoly - 1; i >= 0; i--) {
1875 if (Polys[i]->polyType == TAG) {
1876 PolygonEvent(Common::nullContext, i, STARTUP, 0, false, 0);
1877 }
1878 }
1879 }
1880
1881 // Paths need to know the facts
1882 SetPathAdjacencies();
1883 }
1884 }
1885
1886 /**
1887 * Called at the end of a scene to ditch all polygons.
1888 */
DropPolygons()1889 void DropPolygons() {
1890 pathsOnRoute = 0;
1891 memset(RoutePaths, 0, sizeof(RoutePaths));
1892 RouteEnd = NULL;
1893
1894 for (int i = 0; i < noofPolys; i++) {
1895 if (Polys[i]) {
1896 Polys[i]->pointState = PS_NOT_POINTING;
1897 Polys[i] = NULL;
1898 }
1899 }
1900 noofPolys = 0;
1901 free(Polygons);
1902 Polygons = NULL;
1903 }
1904
1905
1906
PolyType(HPOLYGON hp)1907 PTYPE PolyType(HPOLYGON hp) {
1908 CHECK_HP(hp, "Out of range polygon handle (25)");
1909
1910 return Polys[hp]->polyType;
1911 }
1912
PolySubtype(HPOLYGON hp)1913 int PolySubtype(HPOLYGON hp) {
1914 CHECK_HP(hp, "Out of range polygon handle (26)");
1915
1916 return Polys[hp]->subtype;
1917 }
1918
PolyCenterX(HPOLYGON hp)1919 int PolyCenterX(HPOLYGON hp) {
1920 CHECK_HP(hp, "Out of range polygon handle (27)");
1921
1922 return Polys[hp]->pcenterx;
1923 }
1924
PolyCenterY(HPOLYGON hp)1925 int PolyCenterY(HPOLYGON hp) {
1926 CHECK_HP(hp, "Out of range polygon handle (28)");
1927
1928 return Polys[hp]->pcentery;
1929 }
1930
PolyCornerX(HPOLYGON hp,int n)1931 int PolyCornerX(HPOLYGON hp, int n) {
1932 CHECK_HP(hp, "Out of range polygon handle (29)");
1933
1934 return Polys[hp]->cx[n];
1935 }
1936
PolyCornerY(HPOLYGON hp,int n)1937 int PolyCornerY(HPOLYGON hp, int n) {
1938 CHECK_HP(hp, "Out of range polygon handle (30)");
1939
1940 return Polys[hp]->cy[n];
1941 }
1942
PolyPointState(HPOLYGON hp)1943 PSTATE PolyPointState(HPOLYGON hp) {
1944 CHECK_HP(hp, "Out of range polygon handle (31)");
1945
1946 return Polys[hp]->pointState;
1947 }
1948
PolyTagState(HPOLYGON hp)1949 TSTATE PolyTagState(HPOLYGON hp) {
1950 CHECK_HP(hp, "Out of range polygon handle (32)");
1951
1952 return Polys[hp]->tagState;
1953 }
1954
SetPolyPointState(HPOLYGON hp,PSTATE ps)1955 void SetPolyPointState(HPOLYGON hp, PSTATE ps) {
1956 CHECK_HP(hp, "Out of range polygon handle (34)");
1957
1958 Polys[hp]->pointState = ps;
1959 }
1960
SetPolyTagState(HPOLYGON hp,TSTATE ts)1961 void SetPolyTagState(HPOLYGON hp, TSTATE ts) {
1962 CHECK_HP(hp, "Out of range polygon handle (35)");
1963
1964 Polys[hp]->tagState = ts;
1965 }
1966
SetPolyTagHandle(HPOLYGON hp,SCNHANDLE th)1967 void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th) {
1968 CHECK_HP(hp, "Out of range polygon handle (36)");
1969
1970 Polys[hp]->hOverrideTag = th;
1971 }
1972
MaxPolygons(int numPolys)1973 void MaxPolygons(int numPolys) {
1974 assert(numPolys <= MAX_POLY);
1975
1976 MaxPolys = numPolys;
1977 }
1978
1979 /**
1980 * Get polygon's associated node.
1981 * The one for WalkTag(), StandTag() etc.
1982 */
GetPolyNode(HPOLYGON hp,int * pNodeX,int * pNodeY)1983 void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY) {
1984 CHECK_HP(hp, "GetPolyNode(): Out of range polygon handle");
1985
1986 Poly ptp(LockMem(pHandle), Polys[hp]->pIndex);
1987
1988 // WORKAROUND: Invalid node adjustment for DW2 Cartwheel scene refer polygon
1989 if (TinselV2 && (pHandle == 0x74191900) && (hp == 8)) {
1990 *pNodeX = 480;
1991 *pNodeY = 408;
1992 } else {
1993 *pNodeX = FROM_32(ptp.nodex);
1994 *pNodeY = FROM_32(ptp.nodey);
1995 }
1996
1997 if (TinselV2) {
1998 *pNodeX += volatileStuff[hp].xoff;
1999 *pNodeY += volatileStuff[hp].yoff;
2000 }
2001 }
2002
SetPolyPointedTo(HPOLYGON hp,bool bPointedTo)2003 void SetPolyPointedTo(HPOLYGON hp, bool bPointedTo) {
2004 CHECK_HP(hp, "Out of range polygon handle (34)");
2005
2006 if (bPointedTo)
2007 Polys[hp]->tagFlags |= POINTING;
2008 else
2009 Polys[hp]->tagFlags &= ~POINTING;
2010 }
2011
PolyIsPointedTo(HPOLYGON hp)2012 bool PolyIsPointedTo(HPOLYGON hp) {
2013 CHECK_HP(hp, "Out of range polygon handle (31)");
2014
2015 if (TinselV2)
2016 return (Polys[hp]->tagFlags & POINTING);
2017
2018 return PolyPointState(hp) == PS_POINTING;
2019 }
2020
SetPolyTagWanted(HPOLYGON hp,bool bTagWanted,bool bCursor,SCNHANDLE hOverrideTag)2021 void SetPolyTagWanted(HPOLYGON hp, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag) {
2022 CHECK_HP(hp, "Out of range polygon handle (35)");
2023
2024 if (bTagWanted) {
2025 Polys[hp]->tagFlags |= TAGWANTED;
2026 Polys[hp]->hOverrideTag = hOverrideTag;
2027 } else {
2028 Polys[hp]->tagFlags &= ~TAGWANTED;
2029 Polys[hp]->hOverrideTag = 0;
2030 }
2031
2032 if (bCursor)
2033 Polys[hp]->tagFlags |= FOLLOWCURSOR;
2034 else
2035 Polys[hp]->tagFlags &= ~FOLLOWCURSOR;
2036 }
2037
PolyTagIsWanted(HPOLYGON hp)2038 bool PolyTagIsWanted(HPOLYGON hp) {
2039 CHECK_HP(hp, "Out of range polygon handle (32)");
2040
2041 return (Polys[hp]->tagFlags & TAGWANTED);
2042 }
2043
PolyTagFollowsCursor(HPOLYGON hp)2044 bool PolyTagFollowsCursor(HPOLYGON hp) {
2045 CHECK_HP(hp, "Out of range polygon handle (36)");
2046
2047 return (Polys[hp]->tagFlags & FOLLOWCURSOR);
2048 }
2049
GetPolyTagHandle(HPOLYGON hp)2050 SCNHANDLE GetPolyTagHandle(HPOLYGON hp) {
2051 CHECK_HP(hp, "Out of range polygon handle (33)");
2052
2053 return Polys[hp]->hOverrideTag;
2054 }
2055
IsTagPolygon(int tagno)2056 bool IsTagPolygon(int tagno) {
2057 return (FindPolygon(TAG, tagno) != NOPOLY || FindPolygon(EX_TAG, tagno) != NOPOLY);
2058 }
2059
GetTagPolyId(HPOLYGON hp)2060 int GetTagPolyId(HPOLYGON hp) {
2061 CHECK_HP(hp, "Out of range polygon handle (GetTagPolyId()");
2062
2063 assert(Polys[hp]->polyType == TAG || Polys[hp]->polyType == EX_TAG);
2064
2065 return Polys[hp]->polyID;
2066 }
2067
GetPolyMidBottom(HPOLYGON hp,int * pX,int * pY)2068 void GetPolyMidBottom( HPOLYGON hp, int *pX, int *pY) {
2069 CHECK_HP(hp, "Out of range polygon handle (GetPolyMidBottom()");
2070
2071 *pY = Polys[hp]->pbottom + volatileStuff[hp].yoff;
2072 *pX = (Polys[hp]->pleft + Polys[hp]->pright)/2 + volatileStuff[hp].xoff;
2073 }
2074
PathCount()2075 int PathCount() {
2076 int i, count;
2077
2078 for (i = 0, count = 0; i < noofPolys; i++) {
2079 if (Polys[i]->polyType == PATH)
2080 count++;
2081 }
2082
2083 return count;
2084 }
2085
2086 /**
2087 * Convert a BLOCK to an EX_BLOCK poly
2088 */
DisableBlock(int block)2089 void DisableBlock(int block) {
2090 int i = FindPolygon(BLOCK, block);
2091
2092 if (i != NOPOLY) {
2093 Polys[i]->polyType = EX_BLOCK;
2094 volatileStuff[i].bDead = true;
2095 }
2096 }
2097
2098 /**
2099 * Convert an EX_BLOCK to a BLOCK poly
2100 */
EnableBlock(int block)2101 void EnableBlock(int block) {
2102 int i = FindPolygon(EX_BLOCK, block);
2103
2104 if (i != NOPOLY) {
2105 Polys[i]->polyType = BLOCK;
2106 volatileStuff[i].bDead = false;
2107 }
2108 }
2109
2110 /**
2111 * Convert an EFFECT to an EX_EFFECT poly
2112 */
DisableEffect(int effect)2113 void DisableEffect(int effect) {
2114 int i = FindPolygon(EFFECT, effect);
2115
2116 if (i != NOPOLY) {
2117 Polys[i]->polyType = EX_EFFECT;
2118 volatileStuff[i].bDead = true;
2119 }
2120 }
2121
2122 /**
2123 * Convert an EX_EFFECT to an EFFECT poly
2124 */
EnableEffect(int effect)2125 void EnableEffect(int effect) {
2126 int i = FindPolygon(EX_EFFECT, effect);
2127
2128 if (i != NOPOLY) {
2129 Polys[i]->polyType = EFFECT;
2130 volatileStuff[i].bDead = false;
2131 }
2132 }
2133
2134 /**
2135 * Convert a PATH to an EX_PATH poly
2136 */
DisablePath(int path)2137 void DisablePath(int path) {
2138 int i = FindPolygon(PATH, path);
2139
2140 if (i != NOPOLY) {
2141 Polys[i]->polyType = EX_PATH;
2142 volatileStuff[i].bDead = true;
2143
2144 // Paths need to know the new facts
2145 SetPathAdjacencies();
2146 }
2147 }
2148
2149 /**
2150 * Convert a PATH to an EX_PATH poly
2151 */
EnablePath(int path)2152 void EnablePath(int path) {
2153 int i = FindPolygon(EX_PATH, path);
2154
2155 if (i != NOPOLY) {
2156 Polys[i]->polyType = PATH;
2157 volatileStuff[i].bDead = false;
2158
2159 // Paths need to know the new facts
2160 SetPathAdjacencies();
2161 }
2162 }
2163
2164 /**
2165 * Convert a REFER to an EX_REFER poly
2166 */
DisableRefer(int refer)2167 void DisableRefer(int refer) {
2168 int i = FindPolygon(REFER, refer);
2169
2170 if (i != NOPOLY) {
2171 Polys[i]->polyType = EX_REFER;
2172 volatileStuff[i].bDead = true;
2173 }
2174 }
2175
2176 /**
2177 * Convert a REFER to an EX_REFER poly
2178 */
EnableRefer(int refer)2179 void EnableRefer(int refer) {
2180 int i = FindPolygon(EX_REFER, refer);
2181
2182 if (i != NOPOLY) {
2183 Polys[i]->polyType = REFER;
2184 volatileStuff[i].bDead = false;
2185 }
2186 }
2187
2188 /**
2189 * Convert an EX_TAG to a TAG poly.
2190 */
EnableTag(CORO_PARAM,int tag)2191 void EnableTag(CORO_PARAM, int tag) {
2192 CORO_BEGIN_CONTEXT;
2193 int i;
2194 CORO_END_CONTEXT(_ctx);
2195
2196 CORO_BEGIN_CODE(_ctx);
2197
2198 if ((_ctx->i = FindPolygon(EX_TAG, tag)) != NOPOLY) {
2199 Polys[_ctx->i]->polyType = TAG;
2200 volatileStuff[_ctx->i].bDead = false;
2201
2202 if (TinselV2)
2203 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, SHOWEVENT, 0, true, 0));
2204 } else if ((_ctx->i = FindPolygon(TAG, tag)) != NOPOLY) {
2205 if (TinselV2)
2206 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, SHOWEVENT, 0, true, 0));
2207 }
2208
2209 if (!TinselV2) {
2210 TAGSTATE *pts = &TagStates[SceneTags[currentTScene].offset];
2211 for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
2212 if (pts->tid == tag) {
2213 pts->enabled = true;
2214 break;
2215 }
2216 }
2217 }
2218
2219 CORO_END_CODE;
2220 }
2221
2222 /**
2223 * Convert an EX_EXIT to a EXIT poly.
2224 */
EnableExit(int exitno)2225 void EnableExit(int exitno) {
2226 for (int i = 0; i < MAX_POLY; i++) {
2227 if (Polys[i] && Polys[i]->polyType == EX_EXIT && Polys[i]->polyID == exitno) {
2228 Polys[i]->polyType = EXIT;
2229 }
2230 }
2231
2232 TAGSTATE *pts;
2233 pts = &ExitStates[SceneExits[currentEScene].offset];
2234 for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
2235 if (pts->tid == exitno) {
2236 pts->enabled = true;
2237 break;
2238 }
2239 }
2240 }
2241
2242 /**
2243 * Move a polygon relative to current offset.
2244 */
MovePolygon(PTYPE ptype,int id,int x,int y)2245 void MovePolygon(PTYPE ptype, int id, int x, int y) {
2246 int i = FindPolygon(ptype, id);
2247
2248 // If not found, try its dead equivalent
2249 if (i == NOPOLY) {
2250 switch (ptype) {
2251 case TAG:
2252 ptype = EX_TAG;
2253 break;
2254 default:
2255 break;
2256 }
2257
2258 i = FindPolygon(ptype, id);
2259 }
2260
2261 if (i != NOPOLY) {
2262 volatileStuff[i].xoff += (short)x;
2263 volatileStuff[i].yoff += (short)y;
2264 }
2265 }
2266
2267 /**
2268 * Move a polygon relative to absolute offset.
2269 */
MovePolygonTo(PTYPE ptype,int id,int x,int y)2270 void MovePolygonTo(PTYPE ptype, int id, int x, int y) {
2271 int i = FindPolygon(ptype, id);
2272
2273 // If not found, try its dead equivalent
2274 if (i == NOPOLY) {
2275 switch (ptype) {
2276 case TAG:
2277 ptype = EX_TAG;
2278 break;
2279 default:
2280 break;
2281 }
2282
2283 i = FindPolygon(ptype, id);
2284 }
2285
2286 if (i != NOPOLY) {
2287 volatileStuff[i].xoff = (short)x;
2288 volatileStuff[i].yoff = (short)y;
2289 }
2290 }
2291
2292
2293 /**
2294 * Convert tag number to polygon handle.
2295 */
GetTagHandle(int tagno)2296 HPOLYGON GetTagHandle(int tagno) {
2297 int i = FindPolygon(TAG, tagno);
2298
2299 if (i == NOPOLY)
2300 i = FindPolygon(EX_TAG, tagno);
2301
2302 assert(i != NOPOLY);
2303
2304 return GetPolyHandle(i);
2305 }
2306
2307 /**
2308 * Convert a TAG to an EX_TAG poly.
2309 */
DisableTag(CORO_PARAM,int tag)2310 void DisableTag(CORO_PARAM, int tag) {
2311 CORO_BEGIN_CONTEXT;
2312 int i;
2313 CORO_END_CONTEXT(_ctx);
2314
2315 CORO_BEGIN_CODE(_ctx);
2316
2317 if ((_ctx->i = FindPolygon(TAG, tag)) != NOPOLY) {
2318 Polys[_ctx->i]->polyType = EX_TAG;
2319 Polys[_ctx->i]->tagFlags = 0;
2320 Polys[_ctx->i]->tagState = TAG_OFF;
2321 Polys[_ctx->i]->pointState = PS_NOT_POINTING;
2322
2323 volatileStuff[_ctx->i].bDead = true;
2324
2325 if (TinselV2)
2326 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, HIDEEVENT, 0, true, 0));
2327 } else if ((_ctx->i = FindPolygon(EX_TAG, tag)) != NOPOLY) {
2328 if (TinselV2)
2329 CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->i, HIDEEVENT, 0, true, 0));
2330 }
2331
2332 if (!TinselV2) {
2333 TAGSTATE *pts = &TagStates[SceneTags[currentTScene].offset];
2334 for (int j = 0; j < SceneTags[currentTScene].nooftags; j++, pts++) {
2335 if (pts->tid == tag) {
2336 pts->enabled = false;
2337 break;
2338 }
2339 }
2340 }
2341
2342 CORO_END_CODE;
2343 }
2344
2345 /**
2346 * Convert a EXIT to an EX_EXIT poly.
2347 */
DisableExit(int exitno)2348 void DisableExit(int exitno) {
2349 TAGSTATE *pts;
2350
2351 for (int i = 0; i < MAX_POLY; i++) {
2352 if (Polys[i] && Polys[i]->polyType == EXIT && Polys[i]->polyID == exitno) {
2353 Polys[i]->polyType = EX_EXIT;
2354 Polys[i]->tagState = TAG_OFF;
2355 Polys[i]->pointState = PS_NOT_POINTING;
2356 }
2357 }
2358
2359 pts = &ExitStates[SceneExits[currentEScene].offset];
2360 for (int j = 0; j < SceneExits[currentEScene].nooftags; j++, pts++) {
2361 if (pts->tid == exitno) {
2362 pts->enabled = false;
2363 break;
2364 }
2365 }
2366 }
2367
2368 } // End of namespace Tinsel
2369