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