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