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