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  * Should really be called "moving actors.c"
22  */
23 
24 #include "tinsel/actors.h"
25 #include "tinsel/anim.h"
26 #include "tinsel/background.h"
27 #include "tinsel/config.h"
28 #include "tinsel/dw.h"
29 #include "tinsel/film.h"
30 #include "tinsel/handle.h"
31 #include "tinsel/dialogs.h"
32 #include "tinsel/mareels.h"
33 #include "tinsel/move.h"
34 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
35 #include "tinsel/object.h"
36 #include "tinsel/pcode.h"
37 #include "tinsel/pid.h"
38 #include "tinsel/play.h"
39 #include "tinsel/polygons.h"
40 #include "tinsel/rince.h"
41 #include "tinsel/sched.h"
42 #include "tinsel/sysvar.h"
43 #include "tinsel/timers.h"
44 #include "tinsel/tinsel.h"
45 #include "tinsel/token.h"
46 
47 #include "common/textconsole.h"
48 #include "common/util.h"
49 
50 namespace Tinsel {
51 
52 //----------------- LOCAL GLOBAL DATA --------------------
53 
54 static MOVER g_Movers[MAX_MOVERS];	// FIXME: Avoid non-const global vars
55 
56 //----------------- FUNCTIONS ----------------------------
57 
58 /**
59  * Called from ActorPalette(), normally once just after the beginning of time.
60  */
StoreMoverPalette(PMOVER pMover,int startColor,int length)61 void StoreMoverPalette(PMOVER pMover, int startColor, int length) {
62 	pMover->startColor = startColor;
63 	pMover->paletteLength = length;
64 }
65 
66 /**
67  * Called from the moving actor's main loop.
68  */
CheckBrightness(PMOVER pMover)69 static void CheckBrightness(PMOVER pMover) {
70 	int brightness;
71 
72 	if (pMover->hCpath == NOPOLY || pMover->bHidden)
73 		return;
74 
75 	brightness = GetBrightness(pMover->hCpath, pMover->objY);
76 
77 	if (brightness != pMover->brightness) {
78 		// Do it all immediately on first appearance,
79 		// otherwise do it iteratively
80 
81 		if (pMover->brightness == BOGUS_BRIGHTNESS)
82 			pMover->brightness = brightness;	// all the way
83 		else if (brightness > pMover->brightness)
84 			pMover->brightness++;			// ramp up
85 		else
86 			pMover->brightness--;			// ramp down
87 
88 		DimPartPalette(BgPal(),
89 				pMover->startColor,
90 				pMover->paletteLength,
91 				pMover->brightness);
92 	}
93 }
94 
95 /**
96  * Called from ActorBrightness() Glitter call.
97  * Typically called before the moving actor is created
98  * at the start of a scene to cover a walk-in Play().
99  */
MoverBrightness(PMOVER pMover,int brightness)100 void MoverBrightness(PMOVER pMover, int brightness) {
101 	// Note: Like with some of the Tinsel1 code, this routine original had a process yield
102 	// if BgPal is NULL, and has been changed for ScummVM to a simple assert
103 
104 	// This is changed from a ProcessGiveWay in DW2 to an assert in ScummVM
105 	assert(BgPal());
106 
107 	// Do it all immediately
108 	DimPartPalette(BgPal(), pMover->startColor, pMover->paletteLength, brightness);
109 
110 	// The actor is probably hidden at this point,
111 	pMover->brightness = brightness;
112 }
113 
114 /**
115  * RebootMovers
116  */
RebootMovers()117 void RebootMovers() {
118 	memset(g_Movers, 0, sizeof(g_Movers));
119 }
120 
121 /**
122  * Given an actor number, return pointer to its moving actor structure,
123  * if it is a moving actor.
124  */
GetMover(int ano)125 PMOVER GetMover(int ano) {
126 	int i;
127 
128 	// Slot 0 is reserved for lead actor
129 	if (ano == GetLeadId() || ano == LEAD_ACTOR)
130 		return &g_Movers[0];
131 
132 	for (i = 1; i < MAX_MOVERS; i++)
133 		if (g_Movers[i].actorID == ano)
134 			return &g_Movers[i];
135 
136 	return NULL;
137 }
138 
139 /**
140  * Register an actor as being a moving one.
141  */
RegisterMover(int ano)142 PMOVER RegisterMover(int ano) {
143 	int i;
144 
145 	// Slot 0 is reserved for lead actor
146 	if (ano == GetLeadId() || ano == LEAD_ACTOR) {
147 		g_Movers[0].actorToken = TOKEN_LEAD;
148 		g_Movers[0].actorID = GetLeadId();
149 		return &g_Movers[0];
150 	}
151 
152 	// Check it hasn't already been declared
153 	for (i = 1; i < MAX_MOVERS; i++) {
154 		if (g_Movers[i].actorID == ano) {
155 			// Actor is already a moving actor
156 			return &g_Movers[i];
157 		}
158 	}
159 
160 	// Find an empty slot
161 	for (i = 1; i < MAX_MOVERS; i++)
162 		if (!g_Movers[i].actorID) {
163 			g_Movers[i].actorToken = TOKEN_LEAD + i;
164 			g_Movers[i].actorID = ano;
165 			return &g_Movers[i];
166 		}
167 
168 	error("Too many moving actors");
169 }
170 
171 /**
172  * Given an index, returns the associated moving actor.
173  *
174  * At the time of writing, used by the effect process.
175  */
GetLiveMover(int index)176 PMOVER GetLiveMover(int index) {
177 	assert(index >= 0 && index < MAX_MOVERS); // out of range
178 
179 	if (g_Movers[index].bActive)
180 		return &g_Movers[index];
181 	else
182 		return NULL;
183 }
184 
IsMAinEffectPoly(int index)185 bool IsMAinEffectPoly(int index) {
186 	assert(index >= 0 && index < MAX_MOVERS); // out of range
187 
188 	return g_Movers[index].bInEffect;
189 }
190 
SetMoverInEffect(int index,bool tf)191 void SetMoverInEffect(int index, bool tf) {
192 	assert(index >= 0 && index < MAX_MOVERS); // out of range
193 
194 	g_Movers[index].bInEffect = tf;
195 }
196 
197 /**
198  * Remove a moving actor from the current scene.
199  */
KillMover(PMOVER pMover)200 void KillMover(PMOVER pMover) {
201 	if (pMover->bActive) {
202 		pMover->bActive = false;
203 		MultiDeleteObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
204 		pMover->actorObj = NULL;
205 		assert(CoroScheduler.getCurrentProcess() != pMover->pProc);
206 		CoroScheduler.killProcess(pMover->pProc);
207 	}
208 }
209 
210 /**
211  * getMActorState
212  */
getMActorState(PMOVER pActor)213 bool getMActorState(PMOVER pActor) {
214 	return pActor->bActive;
215 }
216 
217 /**
218  * If the actor's object exists, move it behind the background.
219  * MultiHideObject() is deliberately not used, as StepAnimScript() calls
220  * cause the object to re-appear.
221  */
HideMover(PMOVER pMover,int sf)222 void HideMover(PMOVER pMover, int sf) {
223 	assert(pMover); // Hiding null moving actor
224 
225 	pMover->bHidden = true;
226 
227 	if (!TinselV2) {
228 		// sf is only passed in Tinsel v1
229 		pMover->SlowFactor = sf;
230 	} else {
231 		// Tinsel 2 specific code
232 		if (IsTaggedActor(pMover->actorID)) {
233 			// It may be pointed to
234 			SetActorPointedTo(pMover->actorID, false);
235 			SetActorTagWanted(pMover->actorID, false, false, 0);
236 		}
237 	}
238 
239 	if (pMover->actorObj)
240 		MultiSetZPosition(pMover->actorObj, -1);
241 }
242 
243 /**
244  * MoverHidden
245  */
MoverHidden(PMOVER pMover)246 bool MoverHidden(PMOVER pMover) {
247 	if (pMover)
248 		return pMover->bHidden;
249 	else
250 		return false;
251 }
252 
253 /**
254  * To be or not to be? If it be, then it is.
255  */
MoverIs(PMOVER pMover)256 bool MoverIs(PMOVER pMover) {
257 	if (TinselV2)
258 		return pMover->actorObj ? true : false;
259 	else
260 		return getMActorState(pMover);
261 }
262 
263 /**
264  * To be SWalk()ing or not to be SWalk()ing?
265  */
MoverIsSWalking(PMOVER pMover)266 bool MoverIsSWalking(PMOVER pMover) {
267 	return (MoverMoving(pMover) && pMover->bIgPath);
268 }
269 
270 /**
271  * MoverMoving()
272  */
MoverMoving(PMOVER pMover)273 bool MoverMoving(PMOVER pMover) {
274 	if (!TinselV2)
275 		return pMover->bMoving;
276 
277 	if (pMover->UtargetX == -1 && pMover->UtargetY == -1)
278 		return false;
279 	else
280 		return true;
281 }
282 
283 /**
284  * Return an actor's walk ticket.
285  */
GetWalkNumber(PMOVER pMover)286 int GetWalkNumber(PMOVER pMover) {
287 	return pMover->walkNumber;
288 }
289 
290 /**
291  * GetMoverId
292  */
GetMoverId(PMOVER pMover)293 int GetMoverId(PMOVER pMover) {
294 	return pMover->actorID;
295 }
296 
297 /**
298  * Sets the mover Z position
299  */
SetMoverZ(PMOVER pMover,int y,uint32 zFactor)300 void SetMoverZ(PMOVER pMover, int y, uint32 zFactor) {
301 	if (!pMover->bHidden) {
302 		if (MoverIsSWalking(pMover) && pMover->zOverride != -1) {
303 			// Special for SWalk()
304 			MultiSetZPosition(pMover->actorObj, (pMover->zOverride << ZSHIFT) + y);
305 		} else {
306 			// Normal case
307 			MultiSetZPosition(pMover->actorObj, (zFactor << ZSHIFT) + y);
308 		}
309 	}
310 }
311 
SetMoverZoverride(PMOVER pMover,uint32 zFactor)312 void SetMoverZoverride(PMOVER pMover, uint32 zFactor) {
313 	pMover->zOverride = zFactor;
314 }
315 
316 /**
317  * UnHideMover
318  */
UnHideMover(PMOVER pMover)319 void UnHideMover(PMOVER pMover) {
320 	assert(pMover); // unHiding null moving actor
321 
322 	if (!TinselV2 || pMover->bHidden) {
323 		pMover->bHidden = false;
324 
325 		// Make visible on the screen
326 		if (pMover->actorObj) {
327 			// If no path, just use first path in the scene
328 			if (pMover->hCpath != NOPOLY)
329 				SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
330 			else
331 				SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
332 		}
333 	}
334 }
335 
336 /**
337  * Clear everything out at actor start-up time.
338  */
InitMover(PMOVER pMover)339 static void InitMover(PMOVER pMover) {
340 	pMover->bActive = false;
341 	pMover->actorObj = NULL;
342 	pMover->objX = pMover->objY = 0;
343 
344 	pMover->hRpath = NOPOLY;
345 
346 	pMover->targetX = pMover->targetY = -1;
347 	pMover->ItargetX = pMover->ItargetY = -1;
348 	pMover->hIpath = NOPOLY;
349 	pMover->UtargetX = pMover->UtargetY = -1;
350 	pMover->hUpath = NOPOLY;
351 	pMover->hCpath = NOPOLY;
352 
353 	pMover->over = false;
354 	pMover->InDifficulty = NO_PROB;
355 
356 	pMover->hFnpath = NOPOLY;
357 	pMover->npstatus = NOT_IN;
358 	pMover->line = 0;
359 
360 	pMover->Tline = 0;
361 
362 	if (pMover->direction != FORWARD && pMover->direction != AWAY
363 			&& pMover->direction != LEFTREEL && pMover->direction != RIGHTREEL)
364 		pMover->direction = FORWARD;
365 
366 	if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
367 		pMover->scale = 1;
368 
369 	pMover->brightness = BOGUS_BRIGHTNESS;	// Force initial setup
370 
371 	pMover->bNoPath = false;
372 	pMover->bIgPath = false;
373 	pMover->bHidden = false;	// 20/2/95
374 	pMover->bStop = false;
375 
376 	pMover->walkNumber= 0;
377 	pMover->stepCount = 0;
378 
379 	pMover->bWalkReel = false;
380 	pMover->bSpecReel = false;
381 	pMover->hLastFilm = 0;
382 	pMover->hPushedFilm = 0;
383 
384 	pMover->bInEffect = false;
385 
386 	pMover->walkedFromX = pMover->walkedFromY = 0;
387 }
388 
389 /**
390  * Get it into our heads that there's nothing doing.
391  * Called at the end of a scene.
392  */
DropMovers()393 void DropMovers() {
394 	for (int i = 0; i < MAX_MOVERS; i++)
395 		InitMover(&g_Movers[i]);
396 }
397 
398 
399 /**
400  * Reposition a moving actor.
401  */
PositionMover(PMOVER pMover,int x,int y)402 void PositionMover(PMOVER pMover, int x, int y) {
403 	int	z;
404 	int	node;
405 	HPOLYGON hPath;
406 
407 	assert(pMover); // Moving null moving actor
408 	assert(pMover->actorObj);
409 
410 	pMover->objX = x;
411 	pMover->objY = y;
412 	MultiSetAniXY(pMover->actorObj, x, y);
413 
414 	hPath = InPolygon(x, y, PATH);
415 	if (hPath != NOPOLY) {
416 		pMover->hCpath = hPath;
417 		if (PolySubtype(hPath) == NODE) {
418 			node = NearestNodeWithin(hPath, x, y);
419 			getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
420 			pMover->hFnpath = hPath;
421 			pMover->line = node;
422 			pMover->npstatus = GOING_UP;
423 		} else {
424 			pMover->hFnpath = NOPOLY;
425 			pMover->npstatus = NOT_IN;
426 		}
427 
428 		z = GetScale(hPath, pMover->objY);
429 		pMover->scale = z;
430 		SetMoverStanding(pMover);
431 	} else {
432 		pMover->bNoPath = true;
433 
434 		pMover->hFnpath = NOPOLY;	// Ain't in one
435 		pMover->npstatus = NOT_IN;
436 
437 		// Ensure legal reel and scale
438 		if (pMover->direction < 0 || pMover->direction > 3)
439 			pMover->direction = FORWARD;
440 		if (pMover->scale < 0 || pMover->scale > TOTAL_SCALES)
441 			pMover->scale = 1;
442 	}
443 }
444 
445 /**
446  * Get position of a moving actor.
447  */
GetMoverPosition(PMOVER pMover,int * paniX,int * paniY)448 void GetMoverPosition(PMOVER pMover, int *paniX, int *paniY) {
449 	assert(pMover); // Getting null moving actor's position
450 
451 	if (pMover->actorObj != NULL)
452 		GetAniPosition(pMover->actorObj, paniX, paniY);
453 	else {
454 		*paniX = 0;
455 		*paniY = 0;
456 	}
457 }
458 
459 /**
460  * Moving actor's mid-top position.
461  */
GetMoverMidTop(PMOVER pMover,int * aniX,int * aniY)462 void GetMoverMidTop(PMOVER pMover, int *aniX, int *aniY) {
463 	assert(pMover); // Getting null moving actor's mid-top position
464 	assert(pMover->actorObj); // Getting null moving actor's mid-top position
465 
466 	*aniX = (MultiLeftmost(pMover->actorObj) + MultiRightmost(pMover->actorObj)) / 2;
467 	*aniY = MultiHighest(pMover->actorObj);
468 }
469 
470 /**
471  * Moving actor's left-most co-ordinate.
472  */
GetMoverLeft(PMOVER pMover)473 int GetMoverLeft(PMOVER pMover) {
474 	assert(pMover); // Getting null moving actor's leftmost position
475 	assert(pMover->actorObj); // Getting null moving actor's leftmost position
476 
477 	return MultiLeftmost(pMover->actorObj);
478 }
479 
480 /**
481  * Moving actor's right-most co-ordinate.
482  */
GetMoverRight(PMOVER pMover)483 int GetMoverRight(PMOVER pMover) {
484 	assert(pMover); // Getting null moving actor's rightmost position
485 	assert(pMover->actorObj); // Getting null moving actor's rightmost position
486 
487 	return MultiRightmost(pMover->actorObj);
488 }
489 
490 /**
491  * Moving actor's top co-ordinate.
492  */
GetMoverTop(PMOVER pMover)493 int GetMoverTop(PMOVER pMover) {
494 	assert(pMover); // Getting null moving actor's topmost position
495 	assert(pMover->actorObj); // Getting null moving actor's topmost position
496 
497 	return MultiHighest(pMover->actorObj);
498 }
499 
500 /**
501  * Moving actor's bottom co-ordinate.
502  */
GetMoverBottom(PMOVER pMover)503 int GetMoverBottom(PMOVER pMover) {
504 	assert(pMover); // Getting null moving actor's bottommost position
505 	assert(pMover->actorObj); // Getting null moving actor's bottommost position
506 
507 	return MultiLowest(pMover->actorObj);
508 }
509 
510 /**
511  * See if moving actor is stood within a polygon.
512  */
MoverIsInPolygon(PMOVER pMover,HPOLYGON hp)513 bool MoverIsInPolygon(PMOVER pMover, HPOLYGON hp) {
514 	assert(pMover); // Checking if null moving actor is in polygon
515 	assert(pMover->actorObj); // Checking if null moving actor is in polygon
516 
517 	int aniX, aniY;
518 	GetAniPosition(pMover->actorObj, &aniX, &aniY);
519 
520 	return IsInPolygon(aniX, aniY, hp);
521 }
522 
523 /**
524  * Change which reel is playing for a moving actor.
525  */
AlterMover(PMOVER pMover,SCNHANDLE film,AR_FUNCTION fn)526 void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn) {
527 	const FILM *pfilm;
528 
529 	assert(pMover->actorObj); // Altering null moving actor's animation script
530 
531 	if (fn == AR_POPREEL) {
532 		// Use the saved film
533 		film = pMover->hPushedFilm;
534 	}
535 	if (fn == AR_PUSHREEL) {
536 		// Save the one we're replacing
537 		pMover->hPushedFilm = (pMover->bSpecReel) ? pMover->hLastFilm : 0;
538 	}
539 
540 	if (film == 0) {
541 		if (pMover->bSpecReel) {
542 			// Revert to 'normal' actor
543 			SetMoverWalkReel(pMover, pMover->direction, pMover->scale, true);
544 			pMover->bSpecReel = false;
545 		}
546 	} else {
547 		// Remember this one in case the actor talks
548 		pMover->hLastFilm = film;
549 
550 		pfilm = (const FILM *)LockMem(film);
551 		assert(pfilm != NULL);
552 
553 		InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate));
554 		if (!TinselV2)
555 			pMover->stepCount = 0;
556 
557 		// If no path, just use first path in the scene
558 		if (pMover->hCpath != NOPOLY)
559 			SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
560 		else
561 			SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
562 
563 		if (fn == AR_WALKREEL) {
564 			pMover->bSpecReel = false;
565 			pMover->bWalkReel = true;
566 		} else {
567 			pMover->bSpecReel = true;
568 			pMover->bWalkReel = false;
569 
570 #ifdef DEBUG
571 			assert(StepAnimScript(&pMover->actorAnim) != ScriptFinished); // Actor reel has finished!
572 #else
573 			StepAnimScript(&pMover->actorAnim);	// 04/01/95
574 #endif
575 		}
576 
577 		// Hang on, we may not want him yet! 04/01/95
578 		if (pMover->bHidden)
579 			MultiSetZPosition(pMover->actorObj, -1);
580 	}
581 }
582 
583 /**
584  * Return the actor's direction.
585  */
GetMoverDirection(PMOVER pMover)586 DIRECTION GetMoverDirection(PMOVER pMover) {
587 	return pMover->direction;
588 }
589 
590 /**
591  * Return the actor's scale.
592  */
GetMoverScale(PMOVER pMover)593 int GetMoverScale(PMOVER pMover) {
594 	return pMover->scale;
595 }
596 
597 /**
598  * Point actor in specified derection
599  */
SetMoverDirection(PMOVER pMover,DIRECTION dirn)600 void SetMoverDirection(PMOVER pMover, DIRECTION dirn) {
601 	pMover->direction = dirn;
602 }
603 
604 /**
605  * Get actor to adopt its appropriate standing reel.
606  */
SetMoverStanding(PMOVER pMover)607 void SetMoverStanding(PMOVER pMover) {
608 	assert(pMover->actorObj);
609 	AlterMover(pMover, pMover->standReels[pMover->scale - 1][pMover->direction], AR_NORMAL);
610 }
611 
612 /**
613  * Get actor to adopt its appropriate walking reel.
614  */
SetMoverWalkReel(PMOVER pMover,DIRECTION reel,int scale,bool force)615 void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force) {
616 	SCNHANDLE	whichReel;
617 	const FILM *pfilm;
618 
619 	// Kill off any play that may be going on for this actor
620 	// and restore the real actor
621 	storeActorReel(pMover->actorID, NULL, 0, NULL, 0, 0, 0);
622 	UnHideMover(pMover);
623 
624 	// Don't do it if using a special walk reel
625 	if (pMover->bWalkReel)
626 		return;
627 
628 	if (force || pMover->scale != scale || pMover->direction != reel) {
629 		assert(reel >= 0 && reel <= 3 && scale > 0 && scale <= TOTAL_SCALES); // out of range scale or reel
630 
631 		// If scale change and both are regular scales
632 		// and there's a scaling reel in the right direction
633 		if (pMover->scale != scale
634 				&& scale <= NUM_MAINSCALES && pMover->scale <= NUM_MAINSCALES
635 				&& (whichReel = ScalingReel(pMover->actorID, pMover->scale, scale, reel)) != 0) {
636 //			error("Cripes");
637 			;	// Use what is now in 'whichReel'
638 		} else {
639 			whichReel = pMover->walkReels[scale-1][reel];
640 			assert(whichReel); // no reel
641 		}
642 
643 		pfilm = (const FILM *)LockMem(whichReel);
644 		assert(pfilm != NULL); // no film
645 
646 		InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), 1);
647 
648 		// Synchronised walking reels
649 		assert(pMover->stepCount >= 0);
650 		SkipFrames(&pMover->actorAnim, pMover->stepCount);
651 
652 		pMover->scale = scale;
653 		pMover->direction = reel;
654 	}
655 }
656 
657 /**
658  * Sort some stuff out at actor start-up time.
659  */
InitialPathChecks(PMOVER pMover,int xpos,int ypos)660 static void InitialPathChecks(PMOVER pMover, int xpos, int ypos) {
661 	HPOLYGON hPath;
662 	int	node;
663 	int	z;
664 
665 	pMover->objX = xpos;
666 	pMover->objY = ypos;
667 
668 	/*--------------------------------------
669 	| If Actor is in a follow nodes path,	|
670 	| position it at the nearest node.	|
671 	 --------------------------------------*/
672 	hPath = InPolygon(xpos, ypos, PATH);
673 
674 	if (hPath != NOPOLY) {
675 		pMover->hCpath = hPath;
676 		if (PolySubtype(hPath) == NODE) {
677 			node = NearestNodeWithin(hPath, xpos, ypos);
678 			getNpathNode(hPath, node, &pMover->objX, &pMover->objY);
679 			pMover->hFnpath = hPath;
680 			pMover->line = node;
681 			pMover->npstatus = GOING_UP;
682 		}
683 
684 		z = GetScale(hPath, pMover->objY);
685 	} else {
686 		pMover->bNoPath = true;
687 
688 		z = GetScale(FirstPathPoly(), pMover->objY);
689 	}
690 	SetMoverWalkReel(pMover, FORWARD, z, false);
691 }
692 
MoverProcessHelper(int X,int Y,int id,PMOVER pMover)693 static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) {
694 	const FILM *pfilm;
695 	const MULTI_INIT *pmi;
696 	const FRAME *pFrame;
697 	IMAGE *pim;
698 
699 
700 	assert(BgPal()); // Can't start actor without a background palette
701 	assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels
702 
703 	InitMover(pMover);
704 	InitialPathChecks(pMover, X, Y);
705 
706 	pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]);
707 	pmi = (const MULTI_INIT *)LockMem(FROM_32(pfilm->reels[0].mobj));
708 
709 //---
710 	pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame));
711 
712 	// get pointer to image
713 	pim = (IMAGE *)LockMem(READ_32(pFrame));	// handle to image
714 	pim->hImgPal = TO_32(BgPal());
715 //---
716 	pMover->actorObj = MultiInitObject(pmi);
717 
718 /**/	assert(pMover->actorID == id);
719 	pMover->actorID = id;
720 
721 	// add it to display list
722 	MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
723 	storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0);
724 
725 	InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate));
726 	pMover->stepCount = 0;
727 
728 	MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);
729 
730 	// If no path, just use first path in the scene
731 	if (pMover->hCpath != NOPOLY)
732 		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
733 	else
734 		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
735 
736 	// Make him the right size
737 	SetMoverStanding(pMover);
738 
739 //**** if added 18/11/94, am
740 	if (X != MAGICX && Y != MAGICY) {
741 		HideMover(pMover, 0);		// Allows a play to come in before this appears
742 		pMover->bHidden = false;	// ...but don't stay hidden
743 	}
744 
745 	pMover->bActive = true;
746 }
747 
748 /**
749  * Moving actor process - 1 per moving actor in current scene.
750  */
T1MoverProcess(CORO_PARAM,const void * param)751 void T1MoverProcess(CORO_PARAM, const void *param) {
752 	// COROUTINE
753 	CORO_BEGIN_CONTEXT;
754 	CORO_END_CONTEXT(_ctx);
755 
756 	const PMOVER pActor = *(const PMOVER *)param;
757 
758 	CORO_BEGIN_CODE(_ctx);
759 
760 	while (1) {
761 		if (pActor->bSpecReel) {
762 			if (!pActor->bHidden)
763 #ifdef DEBUG
764 			assert(StepAnimScript(&pActor->actorAnim) != ScriptFinished); // Actor reel has finished!
765 #else
766 			StepAnimScript(&pActor->actorAnim);
767 #endif
768 		} else
769 			DoMoveActor(pActor);
770 
771 		CORO_SLEEP(1);		// allow rescheduling
772 
773 	}
774 
775 	CORO_END_CODE;
776 }
777 
778 /**
779  * Tinsel 2 Moving actor process
780  * - 1 per moving actor in current scene.
781  */
T2MoverProcess(CORO_PARAM,const void * param)782 void T2MoverProcess(CORO_PARAM, const void *param) {
783 	CORO_BEGIN_CONTEXT;
784 	CORO_END_CONTEXT(_ctx);
785 
786 	// Get the co-ordinates - copied to process when it was created
787 	const MAINIT *rpos = (const MAINIT *)param;
788 	PMOVER pMover = rpos->pMover;
789 	int i;
790 	FILM *pFilm;
791 	PMULTI_INIT pmi;
792 
793 	CORO_BEGIN_CODE(_ctx);
794 
795 	for (i = 0; i < TOTAL_SCALES; i++) {
796 		if (pMover->walkReels[i][FORWARD])
797 			break;
798 	}
799 	assert(i < TOTAL_SCALES);
800 
801 	InitMover(pMover);
802 	InitialPathChecks(pMover, rpos->X, rpos->Y);
803 
804 	pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]);	// Any old reel
805 	pmi = (PMULTI_INIT)LockMem(FROM_32(pFilm->reels[0].mobj));
806 
807 	// Poke in the background palette
808 	PokeInPalette(pmi);
809 
810 	pMover->actorObj = MultiInitObject(pmi);
811 	// FIXME: This is what the original did. A bug, perhaps?
812 	// pMover->actorID = pMover->actorID;
813 	pMover->bActive = true;
814 
815 	// add it to display list
816 	MultiInsertObject( GetPlayfieldList(FIELD_WORLD), pMover->actorObj );
817 
818 	InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate);
819 	pMover->stepCount = 0;
820 
821 	MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);
822 
823 	// If no path, just use first path in the scene
824 	if (pMover->hCpath != NOPOLY)
825 		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
826 	else
827 		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));
828 
829 	// Make him the right size
830 	SetMoverStanding(pMover);
831 
832 	HideMover(pMover);		// Allows a play to come in before this appears
833 	pMover->bHidden = false;	// ...but don't stay hidden
834 
835 	for (;;) {
836 		if (pMover->bSpecReel) {
837 			if (!pMover->bHidden)
838 				StepAnimScript(&pMover->actorAnim);
839 		} else
840 			DoMoveActor(pMover);
841 
842 		CheckBrightness(pMover);
843 
844 		CORO_SLEEP(1);
845 	}
846 
847 	CORO_END_CODE;
848 }
849 
850 /**
851  * Creates a handling process for a moving actor
852  */
MoverProcessCreate(int X,int Y,int id,PMOVER pMover)853 void MoverProcessCreate(int X, int Y, int id, PMOVER pMover) {
854 	if (TinselV2) {
855 		MAINIT iStruct;
856 		iStruct.X = X;
857 		iStruct.Y = Y;
858 		iStruct.pMover = pMover;
859 
860 		CoroScheduler.createProcess(PID_MOVER, T2MoverProcess, &iStruct, sizeof(MAINIT));
861 	} else {
862 		MoverProcessHelper(X, Y, id, pMover);
863 		pMover->pProc = CoroScheduler.createProcess(PID_MOVER, T1MoverProcess, &pMover, sizeof(PMOVER));
864 	}
865 }
866 
867 /**
868  * Check for moving actor collision.
869  */
InMoverBlock(PMOVER pMover,int x,int y)870 PMOVER InMoverBlock(PMOVER pMover, int x, int y) {
871 	int	caX;		// Calling actor's pos'n
872 	int	caL, caR;	// Calling actor's left and right
873 	int	taX, taY;	// Test actor's pos'n
874 	int	taL, taR;	// Test actor's left and right
875 
876 	caX = pMover->objX;
877 	if (pMover->hFnpath != NOPOLY || GetNoBlocking())
878 		return NULL;
879 
880 	caL = GetMoverLeft(pMover) + x - caX;
881 	caR = GetMoverRight(pMover) + x - caX;
882 
883 	for (int i = 0; i < MAX_MOVERS; i++) {
884 		if (pMover == &g_Movers[i] ||
885 				(TinselV2 && (g_Movers[i].actorObj == NULL)) ||
886 				(!TinselV2 && !g_Movers[i].bActive))
887 			continue;
888 
889 		// At around the same height?
890 		GetMoverPosition(&g_Movers[i], &taX, &taY);
891 		if (g_Movers[i].hFnpath != NOPOLY)
892 			continue;
893 
894 		if (ABS(y - taY) > 2)	// 2 was 8
895 			continue;
896 
897 		// To the left?
898 		taL = GetMoverLeft(&g_Movers[i]);
899 		if (caR <= taL)
900 			continue;
901 
902 		// To the right?
903 		taR = GetMoverRight(&g_Movers[i]);
904 		if (caL >= taR)
905 			continue;
906 
907 		return &g_Movers[i];
908 	}
909 	return NULL;
910 }
911 
912 /**
913  * Copies key information for savescn.c to store away.
914  */
SaveMovers(SAVED_MOVER * sMoverInfo)915 void SaveMovers(SAVED_MOVER *sMoverInfo) {
916 	for (int i = 0; i < MAX_MOVERS; i++) {
917 		sMoverInfo[i].bActive = !TinselV2 ? g_Movers[i].bActive : g_Movers[i].actorObj != NULL;
918 		sMoverInfo[i].actorID	= g_Movers[i].actorID;
919 		sMoverInfo[i].objX	= g_Movers[i].objX;
920 		sMoverInfo[i].objY	= g_Movers[i].objY;
921 		sMoverInfo[i].hLastfilm	= g_Movers[i].hLastFilm;
922 
923 		if (TinselV2) {
924 			sMoverInfo[i].bHidden = g_Movers[i].bHidden;
925 			sMoverInfo[i].brightness = g_Movers[i].brightness;
926 			sMoverInfo[i].startColor = g_Movers[i].startColor;
927 			sMoverInfo[i].paletteLength = g_Movers[i].paletteLength;
928 		}
929 
930 		memcpy(sMoverInfo[i].walkReels, g_Movers[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
931 		memcpy(sMoverInfo[i].standReels, g_Movers[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
932 		memcpy(sMoverInfo[i].talkReels, g_Movers[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
933 	}
934 }
935 
RestoreAuxScales(SAVED_MOVER * sMoverInfo)936 void RestoreAuxScales(SAVED_MOVER *sMoverInfo) {
937 	for (int i = 0; i < MAX_MOVERS; i++) {
938 		if (TinselV2)
939 			g_Movers[i].actorID = sMoverInfo[i].actorID;
940 
941 		memcpy(g_Movers[i].walkReels, sMoverInfo[i].walkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
942 		memcpy(g_Movers[i].standReels, sMoverInfo[i].standReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
943 		memcpy(g_Movers[i].talkReels, sMoverInfo[i].talkReels, TOTAL_SCALES * 4 * sizeof(SCNHANDLE));
944 	}
945 }
946 
947 
NextMover(PMOVER pMover)948 PMOVER NextMover(PMOVER pMover) {
949 	int next;
950 
951 	if (pMover == NULL)
952 		next = 0;
953 	else
954 		next = pMover - g_Movers + 1;
955 
956 	if (g_Movers[next].actorID)
957 		return &g_Movers[next];
958 	else
959 		return NULL;
960 }
961 
StopMover(PMOVER pMover)962 void StopMover(PMOVER pMover) {
963 	pMover->bStop = true;
964 	DoMoveActor(pMover);
965 }
966 
967 } // End of namespace Tinsel
968