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