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 * Handles things to do with actors, delegates much moving actor stuff.
22 */
23
24 #include "tinsel/actors.h"
25 #include "tinsel/background.h"
26 #include "tinsel/events.h"
27 #include "tinsel/film.h" // for FREEL
28 #include "tinsel/handle.h"
29 #include "tinsel/dialogs.h" // INV_NOICON
30 #include "tinsel/move.h"
31 #include "tinsel/multiobj.h"
32 #include "tinsel/object.h" // for POBJECT
33 #include "tinsel/pcode.h"
34 #include "tinsel/pid.h"
35 #include "tinsel/play.h"
36 #include "tinsel/polygons.h"
37 #include "tinsel/rince.h"
38 #include "tinsel/sched.h"
39 #include "common/serializer.h"
40 #include "tinsel/sysvar.h"
41 #include "tinsel/tinsel.h"
42 #include "tinsel/token.h"
43
44 #include "common/textconsole.h"
45 #include "common/util.h"
46
47 namespace Tinsel {
48
49
50 //----------------- LOCAL DEFINES --------------------
51
52
53 #include "common/pack-start.h" // START STRUCT PACKING
54
55 /** actor struct - one per actor */
56 struct T1_ACTOR_STRUC {
57 int32 masking; ///< type of actor masking
58 SCNHANDLE hActorId; ///< handle actor ID string index
59 SCNHANDLE hActorCode; ///< handle to actor script
60 } PACKED_STRUCT;
61
62 struct T2_ACTOR_STRUC {
63 SCNHANDLE hActorId; // handle actor ID string index
64 SCNHANDLE hTagText; // tag
65 int32 tagPortionV; // defines tag area
66 int32 tagPortionH; // defines tag area
67 SCNHANDLE hActorCode; // handle to actor script
68 } PACKED_STRUCT;
69
70 #include "common/pack-end.h" // END STRUCT PACKING
71
72 //----------------- LOCAL MACROS ----------------------------
73
74 #define RANGE_CHECK(num) assert(num > 0 && num <= NumActors);
75
76 //----------------- LOCAL GLOBAL DATA --------------------
77
78 #define MAX_REELS 6
79
80 // FIXME: Avoid non-const global vars
81
82 static int LeadActorId = 0; // The lead actor
83
84 static int NumActors = 0; // The total number of actors in the game
85
86 struct ACTORINFO {
87 bool bAlive; // TRUE == alive
88 bool bHidden; // TRUE == hidden
89 bool completed; // TRUE == script played out
90
91 int x, y, z;
92
93 int32 mtype; // DEFAULT(b'ground), MASK, ALWAYS
94 SCNHANDLE actorCode; // The actor's script
95
96 const FREEL *presReel; // the present reel
97 int presRnum; // the present reel number
98 SCNHANDLE presFilm; // the film that reel belongs to
99 OBJECT *presObj; // reference for position information
100 int presPlayX, presPlayY;
101
102 bool tagged; // actor tagged?
103 SCNHANDLE hTag; // handle to tag text
104 int tType; // e.g. TAG_Q1TO3
105
106 bool bEscOn;
107 int escEvent;
108
109 COLORREF textColor; // Text color
110
111 SCNHANDLE playFilm; // revert to this after talks
112 SCNHANDLE talkFilm; // this be deleted in the future!
113 SCNHANDLE latestFilm; // the last film ordered
114 bool bTalking;
115
116 int steps;
117 int loopCount;
118
119 // DW2 new fields and alternates
120 int presColumns[MAX_REELS]; // the present columns
121 OBJECT *presObjs[MAX_REELS]; // reference for position information
122 int filmNum;
123 };
124
125 struct TAGACTOR {
126 // Copies of compiled data
127 int id;
128 SCNHANDLE hTagText; // handle to tag text
129 int32 tagPortionV; // which portion is active
130 int32 tagPortionH; // which portion is active
131 SCNHANDLE hActorCode; // The actor's script
132
133 int tagFlags;
134 SCNHANDLE hOverrideTag; // Override tag.
135 };
136 typedef TAGACTOR *PTAGACTOR;
137
138
139 static ACTORINFO *actorInfo = NULL;
140
141 static COLORREF defaultColor = 0; // Text color
142
143 static bool bActorsOn = false;
144
145 static int ti = 0;
146
147 #define MAX_TAGACTORS 10
148
149 static TAGACTOR taggedActors[MAX_TAGACTORS];
150
151 static int numTaggedActors = 0;
152
153 static uint8 *zFactors = NULL;
154
155 static Z_POSITIONS zPositions[NUM_ZPOSITIONS];
156
157 //-------------------- METHOD LIST -----------------------
158
159 /**
160 * Called once at start-up time, and again at restart time.
161 * Registers the total number of actors in the game.
162 * @param num Chunk Id
163 */
RegisterActors(int num)164 void RegisterActors(int num) {
165 if (actorInfo == NULL) {
166 // Store the total number of actors in the game
167 NumActors = num;
168
169 // Check we can save so many
170 assert(NumActors <= MAX_SAVED_ALIVES);
171
172 // Allocate RAM for actor structures
173
174 // FIXME: For now, we always allocate MAX_SAVED_ALIVES blocks,
175 // as this makes the save/load code simpler
176 // size of ACTORINFO is 148, so this allocates 512 * 148 = 75776 bytes, about 74KB
177 actorInfo = (ACTORINFO *)calloc(MAX_SAVED_ALIVES, sizeof(ACTORINFO));
178 if (TinselV2)
179 zFactors = (uint8 *)malloc(MAX_SAVED_ALIVES);
180
181 // make sure memory allocated
182 if (actorInfo == NULL) {
183 error("Cannot allocate memory for actors");
184 }
185 } else {
186 // Check the total number of actors is still the same
187 assert(num == NumActors);
188
189 memset(actorInfo, 0, MAX_SAVED_ALIVES * sizeof(ACTORINFO));
190 if (TinselV2)
191 memset(zFactors, 0, MAX_SAVED_ALIVES);
192 }
193
194 // All actors start off alive.
195 while (num--)
196 actorInfo[num].bAlive = true;
197 }
198
FreeActors()199 void FreeActors() {
200 free(actorInfo);
201 actorInfo = NULL;
202 if (TinselV2) {
203 free(zFactors);
204 zFactors = NULL;
205 }
206 }
207
208 /**
209 * Called from dec_lead(), i.e. normally once at start of master script.
210 * @param leadID Lead Id
211 */
SetLeadId(int leadID)212 void SetLeadId(int leadID) {
213 LeadActorId = leadID;
214 actorInfo[leadID-1].mtype = ACT_MASK;
215 }
216
217 /**
218 * No comment.
219 */
GetLeadId()220 int GetLeadId() {
221 return LeadActorId;
222 }
223
ActorIsGhost(int actor)224 bool ActorIsGhost(int actor) {
225 return actor == SysVar(ISV_GHOST_ACTOR);
226 }
227
228 struct ATP_INIT {
229 int id; // Actor number
230 TINSEL_EVENT event; // Event
231 PLR_EVENT bev; // Causal mouse event
232
233 PINT_CONTEXT pic;
234 };
235
236 /**
237 * Convert actor id to index into TaggedActors[]
238 */
TaggedActorIndex(int actor)239 static int TaggedActorIndex(int actor) {
240 int i;
241
242 for (i = 0; i < numTaggedActors; i++) {
243 if (taggedActors[i].id == actor)
244 return i;
245 }
246
247 error("You may say to yourself \"this is not my tagged actor\"");
248 }
249
250 /**
251 * Runs actor's glitter code.
252 */
ActorTinselProcess(CORO_PARAM,const void * param)253 static void ActorTinselProcess(CORO_PARAM, const void *param) {
254 // COROUTINE
255 CORO_BEGIN_CONTEXT;
256 INT_CONTEXT *pic;
257 bool bTookControl;
258 CORO_END_CONTEXT(_ctx);
259
260 // get the stuff copied to process when it was created
261 const ATP_INIT *atp = (const ATP_INIT *)param;
262
263 CORO_BEGIN_CODE(_ctx);
264
265 if (TinselV2) {
266 // Take control for CONVERSE events
267 if (atp->event == CONVERSE) {
268 _ctx->bTookControl = GetControl();
269 HideConversation(true);
270 } else
271 _ctx->bTookControl = false;
272
273 // Run the Glitter code
274 CORO_INVOKE_1(Interpret, atp->pic);
275
276 // Restore conv window if applicable
277 if (atp->event == CONVERSE) {
278 // Free control if we took it
279 if (_ctx->bTookControl)
280 ControlOn();
281
282 HideConversation(false);
283 }
284 } else {
285 CORO_INVOKE_1(AllowDclick, atp->bev); // May kill us if single click
286
287 // Run the Glitter code
288 assert(actorInfo[atp->id - 1].actorCode); // no code to run
289
290 _ctx->pic = InitInterpretContext(GS_ACTOR, actorInfo[atp->id - 1].actorCode,
291 atp->event, NOPOLY, atp->id, NULL);
292 CORO_INVOKE_1(Interpret, _ctx->pic);
293
294 // If it gets here, actor's code has run to completion
295 actorInfo[atp->id - 1].completed = true;
296 }
297
298 CORO_END_CODE;
299 }
300
301
302 //---------------------------------------------------------------------------
303
304 struct RATP_INIT {
305 INT_CONTEXT *pic;
306 int id; // Actor number
307 };
308
ActorRestoredProcess(CORO_PARAM,const void * param)309 static void ActorRestoredProcess(CORO_PARAM, const void *param) {
310 // COROUTINE
311 CORO_BEGIN_CONTEXT;
312 INT_CONTEXT *pic;
313 CORO_END_CONTEXT(_ctx);
314
315 // get the stuff copied to process when it was created
316 const RATP_INIT *r = (const RATP_INIT *)param;
317 bool isSavegame = r->pic->resumeState == RES_SAVEGAME;
318
319 CORO_BEGIN_CODE(_ctx);
320
321 _ctx->pic = RestoreInterpretContext(r->pic);
322
323 // The newly added check here specially sets the process to RES_NOT when loading a savegame.
324 // This is needed particularly for the Psychiatrist scene in Discworld 1 - otherwise Rincewind
325 // can't go upstairs without leaving the building and returning. If this patch causes problems
326 // in other scenes, an added check for the hCode == 1174490602 could be added.
327 if (isSavegame && TinselV1)
328 _ctx->pic->resumeState = RES_NOT;
329
330 CORO_INVOKE_1(Interpret, _ctx->pic);
331
332 // If it gets here, actor's code has run to completion
333 actorInfo[r->id - 1].completed = true;
334
335 CORO_END_CODE;
336 }
337
RestoreActorProcess(int id,INT_CONTEXT * pic,bool savegameFlag)338 void RestoreActorProcess(int id, INT_CONTEXT *pic, bool savegameFlag) {
339 RATP_INIT r = { pic, id };
340 if (savegameFlag)
341 pic->resumeState = RES_SAVEGAME;
342
343 CoroScheduler.createProcess(PID_TCODE, ActorRestoredProcess, &r, sizeof(r));
344 }
345
346 /**
347 * Starts up process to runs actor's glitter code.
348 * @param ano Actor Id
349 * @param event Event structure
350 * @param be ButEvent
351 */
ActorEvent(int ano,TINSEL_EVENT event,PLR_EVENT be)352 void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be) {
353 ATP_INIT atp;
354
355 // Only if there is Glitter code associated with this actor.
356 if (actorInfo[ano - 1].actorCode) {
357 atp.id = ano;
358 atp.event = event;
359 atp.bev = be;
360 atp.pic = NULL;
361 CoroScheduler.createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
362 }
363 }
364
365 /**
366 * Starts up process to run actor's glitter code.
367 */
ActorEvent(CORO_PARAM,int ano,TINSEL_EVENT tEvent,bool bWait,int myEscape,bool * result)368 void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result) {
369 ATP_INIT atp;
370 int index;
371 CORO_BEGIN_CONTEXT;
372 Common::PPROCESS pProc;
373 CORO_END_CONTEXT(_ctx);
374
375 CORO_BEGIN_CODE(_ctx);
376
377 index = TaggedActorIndex(ano);
378 assert(taggedActors[index].hActorCode);
379 if (result) *result = false;
380
381 atp.id = 0;
382 atp.event = tEvent;
383 atp.pic = InitInterpretContext(GS_ACTOR,
384 taggedActors[index].hActorCode,
385 tEvent,
386 NOPOLY, // No polygon
387 ano, // Actor
388 NULL, // No object
389 myEscape);
390
391 if (atp.pic != NULL) {
392 _ctx->pProc = CoroScheduler.createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
393 AttachInterpret(atp.pic, _ctx->pProc);
394
395 if (bWait)
396 CORO_INVOKE_2(WaitInterpret,_ctx->pProc, result);
397 }
398
399 CORO_END_CODE;
400 }
401
402
403 /**
404 * Called at the start of each scene for each actor with a code block.
405 * @param as Actor structure
406 * @param bRunScript Flag for whether to run actor's script for the scene
407 */
StartActor(const T1_ACTOR_STRUC * as,bool bRunScript)408 void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) {
409 SCNHANDLE hActorId = FROM_32(as->hActorId);
410
411 // Zero-out many things
412 actorInfo[hActorId - 1].bHidden = false;
413 actorInfo[hActorId - 1].completed = false;
414 actorInfo[hActorId - 1].x = 0;
415 actorInfo[hActorId - 1].y = 0;
416 actorInfo[hActorId - 1].presReel = NULL;
417 actorInfo[hActorId - 1].presFilm = 0;
418 actorInfo[hActorId - 1].presObj = NULL;
419
420 // Store current scene's parameters for this actor
421 actorInfo[hActorId - 1].mtype = FROM_32(as->masking);
422 actorInfo[hActorId - 1].actorCode = FROM_32(as->hActorCode);
423
424 // Run actor's script for this scene
425 if (bRunScript) {
426 if (bActorsOn)
427 actorInfo[hActorId - 1].bAlive = true;
428
429 if (actorInfo[hActorId - 1].bAlive && FROM_32(as->hActorCode))
430 ActorEvent(hActorId, STARTUP, PLR_NOEVENT);
431 }
432 }
433
434 /**
435 * Called at the start of each scene. Start each actor with a code block.
436 * @param ah Scene handle
437 * @param numActors Number of actors
438 * @param bRunScript Flag for whether to run actor scene scripts
439 */
StartTaggedActors(SCNHANDLE ah,int numActors,bool bRunScript)440 void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript) {
441 int i;
442
443 if (TinselV2) {
444 // Clear it all out for a fresh start
445 memset(taggedActors, 0, sizeof(taggedActors));
446 numTaggedActors = numActors;
447 } else {
448 // Only actors with code blocks got (x, y) re-initialized, so...
449 for (i = 0; i < NumActors; i++) {
450 actorInfo[i].x = actorInfo[i].y = 0;
451 actorInfo[i].mtype = 0;
452 }
453 }
454
455 if (!TinselV2) {
456 // Tinsel 1 load variation
457 const T1_ACTOR_STRUC *as = (const T1_ACTOR_STRUC *)LockMem(ah);
458 for (i = 0; i < numActors; i++, as++) {
459 StartActor(as, bRunScript);
460 }
461 } else if (numActors > 0) {
462 // Tinsel 2 load variation
463 const T2_ACTOR_STRUC *as = (T2_ACTOR_STRUC *)LockMem(ah);
464 for (i = 0; i < numActors; i++, as++) {
465 assert(as->hActorCode);
466
467 // Store current scene's parameters for this tagged actor
468 taggedActors[i].id = FROM_32(as->hActorId);
469 taggedActors[i].hTagText = FROM_32(as->hTagText);
470 taggedActors[i].tagPortionV = FROM_32(as->tagPortionV);
471 taggedActors[i].tagPortionH = FROM_32(as->tagPortionH);
472 taggedActors[i].hActorCode = FROM_32(as->hActorCode);
473
474 // Run actor's script for this scene
475 if (bRunScript) {
476 // Send in reverse order - they get swapped round in the scheduler
477 ActorEvent(Common::nullContext, taggedActors[i].id, SHOWEVENT, false, 0);
478 ActorEvent(Common::nullContext, taggedActors[i].id, STARTUP, false, 0);
479 }
480 }
481 }
482 }
483
484 /**
485 * Called between scenes, zeroises all actors.
486 */
DropActors()487 void DropActors() {
488
489 for (int i = 0; i < NumActors; i++) {
490 if (TinselV2) {
491 // Save text color
492 COLORREF tColor = actorInfo[i].textColor;
493
494 memset(&actorInfo[i], 0, sizeof(ACTORINFO));
495
496 // Restor text color
497 actorInfo[i].textColor = tColor;
498
499 // Clear extra arrays
500 memset(zFactors, 0, NumActors);
501 memset(zPositions, 0, sizeof(zPositions));
502 } else {
503 // In Tinsel v1, only certain fields get reset
504 actorInfo[i].actorCode = 0; // No script
505 actorInfo[i].presReel = NULL; // No reel running
506 actorInfo[i].presFilm = 0; // ditto
507 actorInfo[i].presObj = NULL; // No object
508 actorInfo[i].x = 0; // No position
509 actorInfo[i].y = 0; // ditto
510
511 actorInfo[i].talkFilm = 0;
512 actorInfo[i].latestFilm = 0;
513 actorInfo[i].playFilm = 0;
514 actorInfo[i].bTalking = false;
515 }
516 }
517 }
518
519 /**
520 * Kill actors.
521 * @param ano Actor Id
522 */
DisableActor(int ano)523 void DisableActor(int ano) {
524 PMOVER pActor;
525
526 assert(ano > 0 && ano <= NumActors); // illegal actor number
527
528 actorInfo[ano - 1].bAlive = false; // Record as dead
529 actorInfo[ano - 1].x = actorInfo[ano - 1].y = 0;
530
531 // Kill off moving actor properly
532 pActor = GetMover(ano);
533 if (pActor)
534 KillMover(pActor);
535 }
536
537 /**
538 * Enable actors.
539 * @param ano Actor Id
540 */
EnableActor(int ano)541 void EnableActor(int ano) {
542 assert(ano > 0 && ano <= NumActors); // illegal actor number
543
544 // Re-incarnate only if it's dead, or it's script ran to completion
545 if (!actorInfo[ano - 1].bAlive || actorInfo[ano - 1].completed) {
546 actorInfo[ano - 1].bAlive = true;
547 actorInfo[ano - 1].bHidden = false;
548 actorInfo[ano - 1].completed = false;
549
550 // Re-run actor's script for this scene
551 if (actorInfo[ano-1].actorCode)
552 ActorEvent(ano, STARTUP, PLR_NOEVENT);
553 }
554 }
555
556 /**
557 * Returns the aliveness (to coin a word) of the actor.
558 * @param ano Actor Id
559 */
actorAlive(int ano)560 bool actorAlive(int ano) {
561 assert(ano > 0 && ano <= NumActors); // illegal actor number
562
563 return actorInfo[ano - 1].bAlive;
564 }
565
566 /**
567 * Define an actor as being tagged.
568 * @param ano Actor Id
569 * @param tagtext Scene handle
570 * @param tp tType
571 */
Tag_Actor(int ano,SCNHANDLE tagtext,int tp)572 void Tag_Actor(int ano, SCNHANDLE tagtext, int tp) {
573 assert(ano > 0 && ano <= NumActors); // illegal actor number
574
575 actorInfo[ano-1].tagged = true;
576 actorInfo[ano-1].hTag = tagtext;
577 actorInfo[ano-1].tType = tp;
578 }
579
580 /**
581 * Undefine an actor as being tagged.
582 * @param ano Actor Id
583 * @param tagtext Scene handle
584 * @param tp tType
585 */
UnTagActor(int ano)586 void UnTagActor(int ano) {
587 assert(ano > 0 && ano <= NumActors); // illegal actor number
588
589 actorInfo[ano-1].tagged = false;
590 }
591
592 /**
593 * Redefine an actor as being tagged.
594 * @param ano Actor Id
595 * @param tagtext Scene handle
596 * @param tp tType
597 */
ReTagActor(int ano)598 void ReTagActor(int ano) {
599 assert(ano > 0 && ano <= NumActors); // illegal actor number
600
601 if (actorInfo[ano-1].hTag)
602 actorInfo[ano-1].tagged = true;
603 }
604
605 /**
606 * Returns a tagged actor's tag type. e.g. TAG_Q1TO3
607 * @param ano Actor Id
608 */
TagType(int ano)609 int TagType(int ano) {
610 assert(ano > 0 && ano <= NumActors); // illegal actor number
611
612 return actorInfo[ano-1].tType;
613 }
614
615 /**
616 * Returns handle to tagged actor's tag text
617 * @param ano Actor Id
618 */
GetActorTag(int ano)619 SCNHANDLE GetActorTag(int ano) {
620 assert(ano > 0 && ano <= NumActors); // illegal actor number
621
622 return actorInfo[ano - 1].hTag;
623 }
624
625 /**
626 * Called from TagProcess, FirstTaggedActor() resets the index, then
627 * NextTagged Actor is repeatedly called until the caller gets fed up
628 * or there are no more tagged actors to look at.
629 */
FirstTaggedActor()630 void FirstTaggedActor() {
631 ti = 0;
632 }
633
634 /**
635 * Called from TagProcess, FirstTaggedActor() resets the index, then
636 * NextTagged Actor is repeatedly called until the caller gets fed up
637 * or there are no more tagged actors to look at.
638 */
NextTaggedActor()639 int NextTaggedActor() {
640 PMOVER pActor;
641 bool hid;
642
643 while (ti < NumActors) {
644 if (actorInfo[ti].tagged) {
645 pActor = GetMover(ti+1);
646 if (pActor)
647 hid = MoverHidden(pActor);
648 else
649 hid = actorInfo[ti].bHidden;
650
651 if (!hid) {
652 return ++ti;
653 }
654 }
655 ++ti;
656 }
657
658 return 0;
659 }
660
661 /**
662 * Called from TagProcess, NextTaggedActor() is
663 * called repeatedly until the caller gets fed up or
664 * there are no more tagged actors to look at.
665 */
NextTaggedActor(int previous)666 int NextTaggedActor(int previous) {
667 PMOVER pMover;
668
669 // Convert actor number to index
670 if (!previous)
671 previous = -1;
672 else
673 previous = TaggedActorIndex(previous);
674
675 while (++previous < numTaggedActors) {
676 pMover = GetMover(taggedActors[previous].id);
677
678 // No tag on lead actor while he's moving
679 if ((taggedActors[previous].id) == GetLeadId() && MoverMoving(pMover)) {
680 taggedActors[previous].tagFlags &= ~(POINTING | TAGWANTED);
681 continue;
682 }
683
684 // Not if the actor doesn't exist at the moment
685 if (pMover && !MoverIs(pMover))
686 continue;
687
688 if (!(pMover ? MoverHidden(pMover) : ActorHidden(taggedActors[previous].id))) {
689 return taggedActors[previous].id;
690 }
691 }
692
693 return 0;
694 }
695
696 /**
697 * Returns the masking type of the actor.
698 * @param ano Actor Id
699 */
actorMaskType(int ano)700 int32 actorMaskType(int ano) {
701 assert(ano > 0 && ano <= NumActors); // illegal actor number
702
703 return actorInfo[ano - 1].mtype;
704 }
705
706 /**
707 * Store/Return the currently stored co-ordinates of the actor.
708 * Delegate the task for moving actors.
709 * @param ano Actor Id
710 * @param x X position
711 * @param y Y position
712 */
StoreActorPos(int ano,int x,int y)713 void StoreActorPos(int ano, int x, int y) {
714 assert(ano > 0 && ano <= NumActors); // illegal actor number
715
716 actorInfo[ano - 1].x = x;
717 actorInfo[ano - 1].y = y;
718 }
719
StoreActorSteps(int ano,int steps)720 void StoreActorSteps(int ano, int steps) {
721 assert(ano > 0 && ano <= NumActors); // illegal actor number
722
723 actorInfo[ano - 1].steps = steps;
724 }
725
GetActorSteps(int ano)726 int GetActorSteps(int ano) {
727 assert(ano > 0 && ano <= NumActors); // illegal actor number
728
729 return actorInfo[ano - 1].steps;
730 }
731
StoreActorZpos(int ano,int z,int column)732 void StoreActorZpos(int ano, int z, int column) {
733 assert(ano > 0 && ano <= NumActors); // illegal actor number
734
735 if (!TinselV2) {
736 // Prior to Tinsel 2, only a single z value was stored
737 actorInfo[ano - 1].z = z;
738 } else {
739 // Alter existing entry, if there is one
740 for (int i = 0; i < NUM_ZPOSITIONS; i++) {
741 if (zPositions[i].actor == ano && zPositions[i].column == column) {
742 zPositions[i].z = z;
743 return;
744 }
745 }
746
747 // No existing entry found, so find an empty slot
748 for (int i = 0; i < NUM_ZPOSITIONS; i++) {
749 if (zPositions[i].actor == 0) {
750 zPositions[i].actor = (short)ano;
751 zPositions[i].column = (short)column;
752 zPositions[i].z = z;
753 return;
754 }
755 }
756
757 error("NUM_ZPOSITIONS exceeded");
758 }
759 }
760
GetActorZpos(int ano,int column)761 int GetActorZpos(int ano, int column) {
762 RANGE_CHECK(ano);
763
764 // Find entry, there should be one
765 for (int i = 0; i < NUM_ZPOSITIONS; i++) {
766 if (zPositions[i].actor == ano && zPositions[i].column == column) {
767 return zPositions[i].z;
768 }
769 }
770
771 return 1000; // Nominal value
772 }
773
IncLoopCount(int ano)774 void IncLoopCount(int ano) {
775 RANGE_CHECK(ano);
776
777 actorInfo[ano - 1].loopCount++;
778 }
779
GetLoopCount(int ano)780 int GetLoopCount(int ano) {
781 RANGE_CHECK(ano);
782
783 return actorInfo[ano - 1].loopCount;
784 }
785
GetActorPos(int ano,int * x,int * y)786 void GetActorPos(int ano, int *x, int *y) {
787 PMOVER pActor;
788
789 assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor
790
791 pActor = GetMover(ano);
792
793 if (pActor)
794 GetMoverPosition(pActor, x, y);
795 else {
796 *x = actorInfo[ano - 1].x;
797 *y = actorInfo[ano - 1].y;
798 }
799 }
800
801 /**
802 * Returns the position of the mid-top of the actor.
803 * Delegate the task for moving actors.
804 * @param ano Actor Id
805 * @param x Output x
806 * @param y Output y
807 */
GetActorMidTop(int ano,int * x,int * y)808 void GetActorMidTop(int ano, int *x, int *y) {
809 // Not used in JAPAN version
810 PMOVER pActor;
811
812 assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor
813
814 pActor = GetMover(ano);
815
816 if (pActor)
817 GetMoverMidTop(pActor, x, y);
818 else if (TinselV2) {
819 *x = (GetActorLeft(ano) + GetActorRight(ano)) / 2;
820 *y = GetActorTop(ano);
821 } else if (actorInfo[ano - 1].presObj) {
822 *x = (MultiLeftmost(actorInfo[ano - 1].presObj)
823 + MultiRightmost(actorInfo[ano - 1].presObj)) / 2;
824 *y = MultiHighest(actorInfo[ano - 1].presObj);
825 } else
826 GetActorPos(ano, x, y); // The best we can do!
827 }
828
829 /**
830 * Return the appropriate co-ordinate of the actor.
831 * @param ano Actor Id
832 */
GetActorLeft(int ano)833 int GetActorLeft(int ano) {
834 assert(ano > 0 && ano <= NumActors); // illegal actor number
835
836 if (!TinselV2) {
837 // Tinsel 1 version
838 if (!actorInfo[ano - 1].presObj)
839 return 0;
840
841 return MultiLeftmost(actorInfo[ano - 1].presObj);
842 }
843
844 // Tinsel 2 version
845 PMOVER pMover = GetMover(ano);
846 int i;
847 bool bIsObj;
848 int left = 0;
849
850 if (pMover != NULL) {
851 return GetMoverLeft(pMover);
852 } else {
853 for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
854 // If there's an object
855 // and it is not a blank frame for it...
856 if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano - 1].presObjs[i])) {
857 if (!bIsObj) {
858 bIsObj = true;
859 left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
860 } else {
861 if (MultiLeftmost(actorInfo[ano - 1].presObjs[i]) < left)
862 left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
863 }
864 }
865 }
866
867 return bIsObj ? left : 0;
868 }
869 }
870
871 /**
872 * Return the appropriate co-ordinate of the actor.
873 * @param ano Actor Id
874 */
GetActorRight(int ano)875 int GetActorRight(int ano) {
876 assert(ano > 0 && ano <= NumActors); // illegal actor number
877
878 if (!TinselV2) {
879 // Tinsel 1 version
880 if (!actorInfo[ano - 1].presObj)
881 return 0;
882
883 return MultiRightmost(actorInfo[ano - 1].presObj);
884 }
885
886 // Tinsel 2 version
887 PMOVER pMover = GetMover(ano);
888 int i;
889 bool bIsObj;
890 int right = 0;
891
892 if (pMover != NULL) {
893 return GetMoverRight(pMover);
894 } else {
895 for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
896 // If there's an object
897 // and it is not a blank frame for it...
898 if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
899 if (!bIsObj) {
900 bIsObj = true;
901 right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
902 } else {
903 if (MultiRightmost(actorInfo[ano-1].presObjs[i]) > right)
904 right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
905 }
906 }
907 }
908 return bIsObj ? right : 0;
909 }
910 }
911
912 /**
913 * Return the appropriate co-ordinate of the actor.
914 * @param ano Actor Id
915 */
GetActorTop(int ano)916 int GetActorTop(int ano) {
917 assert(ano > 0 && ano <= NumActors); // illegal actor number
918
919 if (!TinselV2) {
920 // Tinsel 1 version
921 if (!actorInfo[ano - 1].presObj)
922 return 0;
923
924 return MultiHighest(actorInfo[ano - 1].presObj);
925 }
926
927 // Tinsel 2 version
928 PMOVER pMover = GetMover(ano);
929 int i;
930 bool bIsObj;
931 int top = 0;
932
933 if (pMover != NULL) {
934 return GetMoverTop(pMover);
935 } else {
936 for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
937 // If there's an object
938 // and it is not a blank frame for it...
939 if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
940 if (!bIsObj) {
941 bIsObj = true;
942 top = MultiHighest(actorInfo[ano-1].presObjs[i]);
943 } else {
944 if (MultiHighest(actorInfo[ano-1].presObjs[i]) < top)
945 top = MultiHighest(actorInfo[ano-1].presObjs[i]);
946 }
947 }
948 }
949
950 return bIsObj ? top : 0;
951 }
952 }
953
954 /**
955 * Return the appropriate co-ordinate of the actor.
956 */
GetActorBottom(int ano)957 int GetActorBottom(int ano) {
958 assert(ano > 0 && ano <= NumActors); // illegal actor number
959
960 if (!TinselV2) {
961 // Tinsel 1 version
962 if (!actorInfo[ano - 1].presObj)
963 return 0;
964
965 return MultiLowest(actorInfo[ano - 1].presObj);
966 }
967
968 // Tinsel 2 version
969 PMOVER pMover = GetMover(ano);
970 int i;
971 bool bIsObj;
972 int bottom = 0;
973
974 if (pMover != NULL) {
975 return GetMoverBottom(pMover);
976 } else {
977 for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
978 // If there's an object
979 // and it is not a blank frame for it...
980 if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
981 if (!bIsObj) {
982 bIsObj = true;
983 bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
984 } else {
985 if (MultiLowest(actorInfo[ano-1].presObjs[i]) > bottom)
986 bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
987 }
988 }
989 }
990 return bIsObj ? bottom : 0;
991 }
992 }
993
994 /**
995 * Shows the given actor
996 */
ShowActor(CORO_PARAM,int ano)997 void ShowActor(CORO_PARAM, int ano) {
998 PMOVER pMover;
999 RANGE_CHECK(ano);
1000
1001 CORO_BEGIN_CONTEXT;
1002 CORO_END_CONTEXT(_ctx);
1003
1004 CORO_BEGIN_CODE(_ctx);
1005
1006 // reset hidden flag
1007 actorInfo[ano - 1].bHidden = false;
1008
1009 // Send event to tagged actors
1010 if (IsTaggedActor(ano))
1011 CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, SHOWEVENT, true, 0));
1012
1013 // If moving actor involved, un-hide it
1014 pMover = GetMover(ano);
1015 if (pMover)
1016 UnHideMover(pMover);
1017
1018 CORO_END_CODE;
1019 }
1020
1021 /**
1022 * Set actor hidden status to true.
1023 * For a moving actor, actually hide it.
1024 * @param ano Actor Id
1025 */
HideActor(CORO_PARAM,int ano)1026 void HideActor(CORO_PARAM, int ano) {
1027 PMOVER pMover;
1028 assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
1029
1030 CORO_BEGIN_CONTEXT;
1031 CORO_END_CONTEXT(_ctx);
1032
1033 CORO_BEGIN_CODE(_ctx);
1034
1035 if (TinselV2) {
1036 actorInfo[ano - 1].bHidden = true;
1037
1038 // Send event to tagged actors
1039 // (this is duplicated in HideMover())
1040 if (IsTaggedActor(ano)) {
1041 CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, HIDEEVENT, true, 0));
1042
1043 // It may be pointed to
1044 SetActorPointedTo(ano, false);
1045 SetActorTagWanted(ano, false, false, 0);
1046 }
1047 }
1048
1049 // Get moving actor involved
1050 pMover = GetMover(ano);
1051
1052 if (pMover)
1053 HideMover(pMover, 0);
1054 else if (!TinselV2)
1055 actorInfo[ano - 1].bHidden = true;
1056
1057 CORO_END_CODE;
1058 }
1059
1060 /**
1061 * Return actor hidden status.
1062 */
ActorHidden(int ano)1063 bool ActorHidden(int ano) {
1064 RANGE_CHECK(ano);
1065
1066 return actorInfo[ano - 1].bHidden;
1067 }
1068
1069 /**
1070 * Hide an actor if it's a moving actor.
1071 * @param ano Actor Id
1072 * @param sf sf
1073 */
HideMovingActor(int ano,int sf)1074 bool HideMovingActor(int ano, int sf) {
1075 PMOVER pActor;
1076
1077 assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
1078
1079 // Get moving actor involved
1080 pActor = GetMover(ano);
1081
1082 if (pActor) {
1083 HideMover(pActor, sf);
1084 return true;
1085 } else {
1086 if (actorInfo[ano - 1].presObj != NULL)
1087 MultiHideObject(actorInfo[ano - 1].presObj); // Hidee object
1088 return false;
1089 }
1090 }
1091
1092 /**
1093 * Unhide an actor if it's a moving actor.
1094 * @param ano Actor Id
1095 */
unHideMovingActor(int ano)1096 void unHideMovingActor(int ano) {
1097 PMOVER pActor;
1098
1099 assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
1100
1101 // Get moving actor involved
1102 pActor = GetMover(ano);
1103
1104 assert(pActor); // not a moving actor
1105
1106 UnHideMover(pActor);
1107 }
1108
1109 /**
1110 * Called after a moving actor had been replaced by an splay().
1111 * Moves the actor to where the splay() left it, and continues the
1112 * actor's walk (if any) from the new co-ordinates.
1113 */
restoreMovement(int ano)1114 void restoreMovement(int ano) {
1115 PMOVER pActor;
1116
1117 assert(ano > 0 && ano <= NumActors); // illegal actor number
1118
1119 // Get moving actor involved
1120 pActor = GetMover(ano);
1121
1122 assert(pActor); // not a moving actor
1123
1124 if (pActor->objX == actorInfo[ano - 1].x && pActor->objY == actorInfo[ano - 1].y)
1125 return;
1126
1127 pActor->objX = actorInfo[ano - 1].x;
1128 pActor->objY = actorInfo[ano - 1].y;
1129
1130 if (pActor->actorObj)
1131 SSetActorDest(pActor);
1132 }
1133
1134 /**
1135 * More properly should be called:
1136 * 'store_actor_reel_and/or_film_and/or_object()'
1137 */
storeActorReel(int ano,const FREEL * reel,SCNHANDLE hFilm,OBJECT * pobj,int reelnum,int x,int y)1138 void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y) {
1139 PMOVER pActor;
1140
1141 assert(ano > 0 && ano <= NumActors); // illegal actor number
1142
1143 pActor = GetMover(ano);
1144
1145 // Only store the reel and film for a moving actor if NOT called from MoverProcess()
1146 // (MoverProcess() calls with reel=film=NULL, pobj not NULL)
1147 if (!pActor
1148 || !(reel == NULL && hFilm == 0 && pobj != NULL)) {
1149 actorInfo[ano - 1].presReel = reel; // Store reel
1150 actorInfo[ano - 1].presRnum = reelnum; // Store reel number
1151 actorInfo[ano - 1].presFilm = hFilm; // Store film
1152 actorInfo[ano - 1].presPlayX = x;
1153 actorInfo[ano - 1].presPlayY = y;
1154 }
1155
1156 // Only store the object for a moving actor if called from MoverProcess()
1157 if (!pActor) {
1158 actorInfo[ano - 1].presObj = pobj; // Store object
1159 } else if (reel == NULL && hFilm == 0 && pobj != NULL) {
1160 actorInfo[ano - 1].presObj = pobj; // Store object
1161 }
1162 }
1163
1164 /**
1165 * Return the present reel/film of the actor.
1166 */
actorReel(int ano)1167 const FREEL *actorReel(int ano) {
1168 assert(ano > 0 && ano <= NumActors); // illegal actor number
1169
1170 return actorInfo[ano - 1].presReel; // the present reel
1171 }
1172
1173 /***************************************************************************/
1174
SetActorPlayFilm(int ano,SCNHANDLE hFilm)1175 void SetActorPlayFilm(int ano, SCNHANDLE hFilm) {
1176 assert(ano > 0 && ano <= NumActors); // illegal actor number
1177
1178 actorInfo[ano - 1].playFilm = hFilm;
1179 }
1180
GetActorPlayFilm(int ano)1181 SCNHANDLE GetActorPlayFilm(int ano) {
1182 assert(ano > 0 && ano <= NumActors); // illegal actor number
1183
1184 return actorInfo[ano - 1].playFilm;
1185 }
1186
SetActorTalkFilm(int ano,SCNHANDLE hFilm)1187 void SetActorTalkFilm(int ano, SCNHANDLE hFilm) {
1188 assert(ano > 0 && ano <= NumActors); // illegal actor number
1189
1190 actorInfo[ano - 1].talkFilm = hFilm;
1191 }
1192
GetActorTalkFilm(int ano)1193 SCNHANDLE GetActorTalkFilm(int ano) {
1194 assert(ano > 0 && ano <= NumActors); // illegal actor number
1195
1196 return actorInfo[ano - 1].talkFilm;
1197 }
1198
SetActorTalking(int ano,bool tf)1199 void SetActorTalking(int ano, bool tf) {
1200 assert(ano > 0 && ano <= NumActors); // illegal actor number
1201
1202 actorInfo[ano - 1].bTalking = tf;
1203 }
1204
ActorIsTalking(int ano)1205 bool ActorIsTalking(int ano) {
1206 assert(ano > 0 && ano <= NumActors); // illegal actor number
1207
1208 return actorInfo[ano - 1].bTalking;
1209 }
1210
SetActorLatestFilm(int ano,SCNHANDLE hFilm)1211 void SetActorLatestFilm(int ano, SCNHANDLE hFilm) {
1212 assert(ano > 0 && ano <= NumActors); // illegal actor number
1213
1214 actorInfo[ano - 1].latestFilm = hFilm;
1215 actorInfo[ano - 1].steps = 0;
1216 }
1217
GetActorLatestFilm(int ano)1218 SCNHANDLE GetActorLatestFilm(int ano) {
1219 assert(ano > 0 && ano <= NumActors); // illegal actor number
1220
1221 return actorInfo[ano - 1].latestFilm;
1222 }
1223
1224 /***************************************************************************/
1225
UpdateActorEsc(int ano,bool escOn,int escEvent)1226 void UpdateActorEsc(int ano, bool escOn, int escEvent) {
1227 assert(ano > 0 && ano <= NumActors); // illegal actor number
1228
1229 actorInfo[ano - 1].bEscOn = escOn;
1230 actorInfo[ano - 1].escEvent = escEvent;
1231 }
1232
UpdateActorEsc(int ano,int escEvent)1233 void UpdateActorEsc(int ano, int escEvent) {
1234 RANGE_CHECK(ano);
1235
1236 if (escEvent) {
1237 actorInfo[ano - 1].bEscOn = true;
1238 actorInfo[ano - 1].escEvent = escEvent;
1239 } else {
1240 actorInfo[ano - 1].bEscOn = false;
1241 actorInfo[ano - 1].escEvent = GetEscEvents();
1242 }
1243
1244 }
1245
ActorEsc(int ano)1246 bool ActorEsc(int ano) {
1247 assert(ano > 0 && ano <= NumActors); // illegal actor number
1248
1249 return actorInfo[ano - 1].bEscOn;
1250 }
1251
ActorEev(int ano)1252 int ActorEev(int ano) {
1253 assert(ano > 0 && ano <= NumActors); // illegal actor number
1254
1255 return actorInfo[ano - 1].escEvent;
1256 }
1257
1258 /**
1259 * Guess what these do.
1260 */
AsetZPos(OBJECT * pObj,int y,int32 z)1261 int AsetZPos(OBJECT *pObj, int y, int32 z) {
1262 int zPos;
1263
1264 z += z ? -1 : 0;
1265
1266 zPos = y + (z << ZSHIFT);
1267 MultiSetZPosition(pObj, zPos);
1268 return zPos;
1269 }
1270
1271 /**
1272 * Guess what these do.
1273 */
SetMoverZ(PMOVER pMover,int y,int32 zFactor)1274 void SetMoverZ(PMOVER pMover, int y, int32 zFactor) {
1275 if (!pMover->bHidden) {
1276 if (!TinselV2)
1277 AsetZPos(pMover->actorObj, y, zFactor);
1278 else if (MoverIsSWalking(pMover) && pMover->zOverride != -1) {
1279 // Special for SWalk()
1280 MultiSetZPosition(pMover->actorObj, (pMover->zOverride << ZSHIFT) + y);
1281 } else {
1282 // Normal case
1283 MultiSetZPosition(pMover->actorObj, (zFactor << ZSHIFT) + y);
1284 }
1285 }
1286 }
1287
1288 /**
1289 * Stores actor's attributes.
1290 * Currently only the speech colors.
1291 */
storeActorAttr(int ano,int r1,int g1,int b1)1292 void storeActorAttr(int ano, int r1, int g1, int b1) {
1293 assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number
1294
1295 if (r1 > MAX_INTENSITY) r1 = MAX_INTENSITY; // } Ensure
1296 if (g1 > MAX_INTENSITY) g1 = MAX_INTENSITY; // } within limits
1297 if (b1 > MAX_INTENSITY) b1 = MAX_INTENSITY; // }
1298
1299 if (ano == -1)
1300 defaultColor = TINSEL_RGB(r1, g1, b1);
1301 else
1302 actorInfo[ano - 1].textColor = TINSEL_RGB(r1, g1, b1);
1303 }
1304
1305 /**
1306 * Called from ActorRGB() - Stores actor's speech color.
1307 */
1308
SetActorRGB(int ano,COLORREF color)1309 void SetActorRGB(int ano, COLORREF color) {
1310 assert(ano >= 0 && ano <= NumActors);
1311
1312 if (ano)
1313 actorInfo[ano - 1].textColor = TO_32(color);
1314 else
1315 defaultColor = TO_32(color);
1316 }
1317
1318 /**
1319 * Get the actor's stored speech color.
1320 * @param ano Actor Id
1321 */
GetActorRGB(int ano)1322 COLORREF GetActorRGB(int ano) {
1323 // Not used in JAPAN version
1324 assert((ano >= -1) && (ano <= NumActors)); // illegal actor number
1325
1326 if ((ano == -1) || !actorInfo[ano - 1].textColor)
1327 return defaultColor;
1328 else
1329 return actorInfo[ano - 1].textColor;
1330 }
1331
1332 /**
1333 * Set the actor's Z-factor
1334 */
SetActorZfactor(int ano,uint32 zFactor)1335 void SetActorZfactor(int ano, uint32 zFactor) {
1336 RANGE_CHECK(ano);
1337
1338 zFactors[ano - 1] = (uint8)zFactor;
1339 }
1340
GetActorZfactor(int ano)1341 uint32 GetActorZfactor(int ano) {
1342 RANGE_CHECK(ano);
1343
1344 return zFactors[ano - 1];
1345 }
1346
1347 /**
1348 * Store relevant information pertaining to currently existing actors.
1349 */
SaveActors(SAVED_ACTOR * sActorInfo)1350 int SaveActors(SAVED_ACTOR *sActorInfo) {
1351 int i, j, k;
1352
1353 for (i = 0, j = 0; i < NumActors; i++) {
1354 for (k = 0; k < (TinselV2 ? MAX_REELS : 1); ++k) {
1355 bool presFlag = !TinselV2 ? actorInfo[i].presObj != NULL :
1356 (actorInfo[i].presObjs[k] != NULL) && !IsCdPlayHandle(actorInfo[i].presFilm);
1357 if (presFlag) {
1358
1359 assert(j < MAX_SAVED_ACTORS); // Saving too many actors
1360
1361 if (!TinselV2) {
1362 sActorInfo[j].bAlive = actorInfo[i].bAlive;
1363 sActorInfo[j].zFactor = (short)actorInfo[i].z;
1364 sActorInfo[j].presRnum = (short)actorInfo[i].presRnum;
1365 }
1366
1367 sActorInfo[j].actorID = (short)(i+1);
1368 if (TinselV2)
1369 sActorInfo[j].bHidden = actorInfo[i].bHidden;
1370 // sActorInfo[j].x = (short)actorInfo[i].x;
1371 // sActorInfo[j].y = (short)actorInfo[i].y;
1372 // sActorInfo[j].presReel = actorInfo[i].presReel;
1373 sActorInfo[j].presFilm = actorInfo[i].presFilm;
1374 sActorInfo[j].presPlayX = (short)actorInfo[i].presPlayX;
1375 sActorInfo[j].presPlayY = (short)actorInfo[i].presPlayY;
1376 j++;
1377
1378 break;
1379 }
1380 }
1381 }
1382
1383 return j;
1384 }
1385
1386 /**
1387 * Restore actor data
1388 */
RestoreActors(int numActors,PSAVED_ACTOR sActorInfo)1389 void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo) {
1390 int i, aIndex;
1391
1392 for (i = 0; i < numActors; i++) {
1393 aIndex = sActorInfo[i].actorID - 1;
1394
1395 actorInfo[aIndex].bHidden = sActorInfo[i].bHidden;
1396
1397 // Play the same reel.
1398 if (sActorInfo[i].presFilm != 0) {
1399 RestoreActorReels(sActorInfo[i].presFilm, sActorInfo[i].actorID,
1400 sActorInfo[i].presPlayX, sActorInfo[i].presPlayY);
1401 }
1402 }
1403 }
1404
SaveZpositions(void * zpp)1405 void SaveZpositions(void *zpp) {
1406 memcpy(zpp, zPositions, sizeof(zPositions));
1407 }
1408
RestoreZpositions(void * zpp)1409 void RestoreZpositions(void *zpp) {
1410 memcpy(zPositions, zpp, sizeof(zPositions));
1411 }
1412
SaveActorZ(byte * saveActorZ)1413 void SaveActorZ(byte *saveActorZ) {
1414 assert(NumActors <= MAX_SAVED_ACTOR_Z);
1415
1416 memcpy(saveActorZ, zFactors, NumActors);
1417 }
1418
RestoreActorZ(byte * saveActorZ)1419 void RestoreActorZ(byte *saveActorZ) {
1420 memcpy(zFactors, saveActorZ, NumActors);
1421 }
1422
setactorson()1423 void setactorson() {
1424 bActorsOn = true;
1425 }
1426
ActorsLife(int ano,bool bAlive)1427 void ActorsLife(int ano, bool bAlive) {
1428 assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number
1429
1430 actorInfo[ano-1].bAlive = bAlive;
1431 }
1432
1433
syncAllActorsAlive(Common::Serializer & s)1434 void syncAllActorsAlive(Common::Serializer &s) {
1435 for (int i = 0; i < MAX_SAVED_ALIVES; i++) {
1436 s.syncAsByte(actorInfo[i].bAlive);
1437 s.syncAsByte(actorInfo[i].tagged);
1438 s.syncAsByte(actorInfo[i].tType);
1439 s.syncAsUint32LE(actorInfo[i].hTag);
1440 }
1441 }
1442
1443 /**
1444 * Called from EndActor()
1445 */
dwEndActor(int ano)1446 void dwEndActor(int ano) {
1447 int i;
1448
1449 RANGE_CHECK(ano);
1450
1451 // Make play.c think it's been replaced
1452 // The following line may have been indirectly making text go away!
1453 // actorInfo[ano - 1].presFilm = NULL;
1454 // but things were returning after a cut scene.
1455 // so re-instate it and de-register the object
1456 actorInfo[ano - 1].presFilm = 0;
1457 actorInfo[ano-1].filmNum++;
1458
1459 for (i = 0; i < MAX_REELS; i++) {
1460 // It may take a frame to remove this, so make it invisible
1461 if (actorInfo[ano-1].presObjs[i] != NULL) {
1462 MultiHideObject(actorInfo[ano-1].presObjs[i]);
1463 actorInfo[ano-1].presObjs[i] = NULL;
1464 }
1465 }
1466 }
1467
1468
1469 /**
1470 * Returns a tagged actor's tag portion.
1471 */
GetActorTagPortion(int ano,unsigned * top,unsigned * bottom,unsigned * left,unsigned * right)1472 void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right) {
1473 // Convert actor number to index
1474 ano = TaggedActorIndex(ano);
1475
1476 *top = taggedActors[ano].tagPortionV >> 16;
1477 *bottom = taggedActors[ano].tagPortionV & 0xffff;
1478 *left = taggedActors[ano].tagPortionH >> 16;
1479 *right = taggedActors[ano].tagPortionH & 0xffff;
1480
1481 // ensure validity
1482 assert(*top >= 1 && *top <= 8);
1483 assert(*bottom >= *top && *bottom <= 8);
1484 assert(*left >= 1 && *left <= 8);
1485 assert(*right >= *left && *right <= 8);
1486 }
1487
1488 /**
1489 * Returns handle to tagged actor's tag text.
1490 */
GetActorTagHandle(int ano)1491 SCNHANDLE GetActorTagHandle(int ano) {
1492 // Convert actor number to index
1493 ano = TaggedActorIndex(ano);
1494
1495 return taggedActors[ano].hOverrideTag ?
1496 taggedActors[ano].hOverrideTag : taggedActors[ano].hTagText;
1497 }
1498
SetActorPointedTo(int actor,bool bPointedTo)1499 void SetActorPointedTo(int actor, bool bPointedTo) {
1500 // Convert actor number to index
1501 actor = TaggedActorIndex(actor);
1502
1503 if (bPointedTo)
1504 taggedActors[actor].tagFlags |= POINTING;
1505 else
1506 taggedActors[actor].tagFlags &= ~POINTING;
1507 }
1508
ActorIsPointedTo(int actor)1509 bool ActorIsPointedTo(int actor) {
1510 // Convert actor number to index
1511 actor = TaggedActorIndex(actor);
1512
1513 return (taggedActors[actor].tagFlags & POINTING);
1514 }
1515
SetActorTagWanted(int actor,bool bTagWanted,bool bCursor,SCNHANDLE hOverrideTag)1516 void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag) {
1517 // Convert actor number to index
1518 actor = TaggedActorIndex(actor);
1519
1520 if (bTagWanted) {
1521 taggedActors[actor].tagFlags |= TAGWANTED;
1522 taggedActors[actor].hOverrideTag = hOverrideTag;
1523 } else {
1524 taggedActors[actor].tagFlags &= ~TAGWANTED;
1525 taggedActors[actor].hOverrideTag = 0;
1526 }
1527
1528 if (bCursor)
1529 taggedActors[actor].tagFlags |= FOLLOWCURSOR;
1530 else
1531 taggedActors[actor].tagFlags &= ~FOLLOWCURSOR;
1532 }
1533
ActorTagIsWanted(int actor)1534 bool ActorTagIsWanted(int actor) {
1535 // Convert actor number to index
1536 actor = TaggedActorIndex(actor);
1537
1538 return (taggedActors[actor].tagFlags & TAGWANTED);
1539 }
1540
1541 /**
1542 * Given cursor position and an actor number, ascertains
1543 * whether the cursor is within the actor's tag area.
1544 * Returns True for a positive result, False for negative.
1545 */
InHotSpot(int ano,int curX,int curY)1546 bool InHotSpot(int ano, int curX, int curY) {
1547 int aTop, aBot; // Top and bottom limits }
1548 int aHeight; // Height } of active area
1549 int aLeft, aRight; // Left and right }
1550 int aWidth; // Width }
1551 unsigned topEighth, botEighth, leftEighth, rightEighth;
1552
1553 // First check if within broad range
1554 if (curX < (aLeft = GetActorLeft(ano)) // too far left
1555 || curX > (aRight = GetActorRight(ano)) // too far right
1556 || curY < (aTop = GetActorTop(ano)) // too high
1557 || curY > (aBot = GetActorBottom(ano)) ) // too low
1558 return false;
1559
1560 GetActorTagPortion(ano, &topEighth, &botEighth, &leftEighth, &rightEighth);
1561
1562 aWidth = aRight - aLeft;
1563 aLeft += ((leftEighth - 1)*aWidth)/8;
1564 aRight -= ((8 - rightEighth)*aWidth)/8;
1565
1566 // check if within x-range
1567 if (curX < aLeft || curX > aRight)
1568 return false;
1569
1570 aHeight = aBot - aTop;
1571 aTop += ((topEighth - 1)*aHeight)/8;
1572 aBot -= ((8 - botEighth)*aHeight)/8;
1573
1574 // check if within y-range
1575 if (curY < aTop || curY > aBot)
1576 return false;
1577
1578 return true;
1579 }
1580
1581 /**
1582 * Front Tagged Actor
1583 */
FrontTaggedActor()1584 int FrontTaggedActor() {
1585 int i;
1586
1587 for (i = 0; i < numTaggedActors; i++) {
1588 if (taggedActors[i].tagFlags & POINTING)
1589 return taggedActors[i].id;
1590 }
1591 return 0;
1592 }
1593
1594 /**
1595 * GetActorTagPos
1596 */
GetActorTagPos(int actor,int * pTagX,int * pTagY,bool bAbsolute)1597 void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute) {
1598 unsigned topEighth, botEighth;
1599 int aTop; // Top and bottom limits }
1600 int aHeight; // Height } of active area
1601 int Loffset, Toffset;
1602
1603 GetActorTagPortion(actor, &topEighth, &botEighth, (unsigned *)&Loffset, (unsigned *)&Toffset);
1604
1605 aTop = GetActorTop(actor);
1606 aHeight = GetActorBottom(actor) - aTop;
1607 aTop += ((topEighth - 1) * aHeight) / 8;
1608
1609 *pTagX = ((GetActorLeft(actor) + GetActorRight(actor)) / 2);
1610 *pTagY = aTop;
1611
1612 if (!bAbsolute) {
1613 PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
1614 *pTagX -= Loffset;
1615 *pTagY -= Toffset;
1616 }
1617 }
1618
1619 /**
1620 * Is Tagged Actor
1621 */
IsTaggedActor(int actor)1622 bool IsTaggedActor(int actor) {
1623 int i;
1624
1625 for (i = 0; i < numTaggedActors; i++) {
1626 if (taggedActors[i].id == actor)
1627 return true;
1628 }
1629 return false;
1630 }
1631
1632 /**
1633 * StoreActorPresFilm
1634 */
StoreActorPresFilm(int ano,SCNHANDLE hFilm,int x,int y)1635 void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y) {
1636 int i;
1637
1638 RANGE_CHECK(ano);
1639
1640 actorInfo[ano-1].presFilm = hFilm;
1641 actorInfo[ano-1].presPlayX = x;
1642 actorInfo[ano-1].presPlayY = y;
1643 actorInfo[ano-1].filmNum++;
1644
1645 for (i = 0; i < MAX_REELS; i++) {
1646 // It may take a frame to remove this, so make it invisible
1647 if (actorInfo[ano - 1].presObjs[i] != NULL)
1648 MultiHideObject(actorInfo[ano - 1].presObjs[i]);
1649
1650 actorInfo[ano - 1].presColumns[i] = -1;
1651 actorInfo[ano - 1].presObjs[i] = NULL;
1652 }
1653 }
1654
1655 /**
1656 * GetActorPresFilm
1657 */
GetActorPresFilm(int ano)1658 SCNHANDLE GetActorPresFilm(int ano) {
1659 RANGE_CHECK(ano);
1660
1661 return actorInfo[ano - 1].presFilm;
1662 }
1663
1664
1665 /**
1666 * GetActorFilmNumber
1667 */
GetActorFilmNumber(int ano)1668 int GetActorFilmNumber(int ano) {
1669 RANGE_CHECK(ano);
1670
1671 return actorInfo[ano - 1].filmNum;
1672 }
1673
1674 /**
1675 * More properly should be called:
1676 * 'StoreActorReelAndObject()'
1677 */
StoreActorReel(int actor,int column,OBJECT * pObj)1678 void StoreActorReel(int actor, int column, OBJECT *pObj) {
1679 RANGE_CHECK(actor);
1680 int i;
1681
1682 for (i = 0; i < MAX_REELS; i++) {
1683 if (actorInfo[actor-1].presColumns[i] == -1) {
1684 // Store reel and object
1685 actorInfo[actor - 1].presColumns[i] = column;
1686 actorInfo[actor - 1].presObjs[i] = pObj;
1687 break;
1688 }
1689 }
1690
1691 assert(i < MAX_REELS);
1692 }
1693
1694 /**
1695 * NotPlayingReel
1696 */
NotPlayingReel(int actor,int filmNumber,int column)1697 void NotPlayingReel(int actor, int filmNumber, int column) {
1698 int i;
1699
1700 RANGE_CHECK(actor);
1701
1702 if (actorInfo[actor-1].filmNum != filmNumber)
1703 return;
1704
1705 // De-register this reel
1706 for (i = 0; i < MAX_REELS; i++) {
1707 if (actorInfo[actor-1].presColumns[i] == column) {
1708 actorInfo[actor-1].presObjs[i] = NULL;
1709 actorInfo[actor-1].presColumns[i] = -1;
1710 break;
1711 }
1712 }
1713
1714 // De-register the film if this was the last reel
1715 for (i = 0; i < MAX_REELS; i++) {
1716 if (actorInfo[actor-1].presColumns[i] != -1)
1717 break;
1718 }
1719 if (i == MAX_REELS)
1720 actorInfo[actor-1].presFilm = 0;
1721 }
1722
ActorReelPlaying(int actor,int column)1723 bool ActorReelPlaying(int actor, int column) {
1724 RANGE_CHECK(actor);
1725
1726 for (int i = 0; i < MAX_REELS; i++) {
1727 if (actorInfo[actor - 1].presColumns[i] == column)
1728 return true;
1729 }
1730 return false;
1731 }
1732
1733 } // End of namespace Tinsel
1734