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  * Glitter library functions.
22  *
23  * In the main called only from PCODE.C
24  * Function names are the same as Glitter code function names.
25  *
26  * To ensure exclusive use of resources and exclusive control responsibilities.
27  */
28 
29 #define BODGE
30 
31 #include "common/coroutines.h"
32 #include "tinsel/actors.h"
33 #include "tinsel/background.h"
34 #include "tinsel/bmv.h"
35 #include "tinsel/config.h"
36 #include "tinsel/cursor.h"
37 #include "tinsel/drives.h"
38 #include "tinsel/dw.h"
39 #include "tinsel/events.h"
40 #include "tinsel/faders.h"
41 #include "tinsel/film.h"
42 #include "tinsel/font.h"
43 #include "tinsel/graphics.h"
44 #include "tinsel/handle.h"
45 #include "tinsel/dialogs.h"
46 #include "tinsel/mareels.h"
47 #include "tinsel/move.h"
48 #include "tinsel/multiobj.h"
49 #include "tinsel/music.h"
50 #include "tinsel/object.h"
51 #include "tinsel/palette.h"
52 #include "tinsel/pcode.h"
53 #include "tinsel/pid.h"
54 #include "tinsel/play.h"
55 #include "tinsel/polygons.h"
56 #include "tinsel/rince.h"
57 #include "tinsel/savescn.h"
58 #include "tinsel/sched.h"
59 #include "tinsel/scn.h"
60 #include "tinsel/scroll.h"
61 #include "tinsel/sound.h"
62 #include "tinsel/strres.h"
63 #include "tinsel/sysvar.h"
64 #include "tinsel/text.h"
65 #include "tinsel/timers.h"		// For ONE_SECOND constant
66 #include "tinsel/tinlib.h"
67 #include "tinsel/tinsel.h"
68 #include "tinsel/token.h"
69 
70 #include "common/textconsole.h"
71 
72 namespace Tinsel {
73 
74 //----------------- EXTERNAL GLOBAL DATA --------------------
75 
76 // In DOS_DW.C
77 extern bool g_bRestart;		// restart flag - set to restart the game
78 extern bool g_bHasRestarted;	// Set after a restart
79 
80 // In PCODE.CPP
81 extern bool g_bNoPause;
82 
83 // In DOS_MAIN.C
84 // TODO/FIXME: From dos_main.c: "Only used on PSX so far"
85 //int clRunMode = 0;
86 
87 //----------------- EXTERNAL FUNCTIONS ---------------------
88 
89 // in BG.CPP
90 extern void ChangePalette(SCNHANDLE hPal);
91 
92 // in PDISPLAY.CPP
93 extern void EnableTags();
94 extern void DisableTags();
95 bool DisableTagsIfEnabled();
96 extern void setshowstring();
97 
98 // in SAVELOAD.CPP
99 extern int NewestSavedGame();
100 
101 // in SCENE.CPP
102 extern void setshowpos();
103 extern int g_sceneCtr;
104 
105 // in TINSEL.CPP
106 extern void SetCdChangeScene(SCNHANDLE hScene);
107 extern void SetHookScene(SCNHANDLE scene, int entrance, int transition);
108 extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
109 extern void UnHookScene();
110 extern void SuspendHook();
111 extern void UnSuspendHook();
112 
113 #ifdef BODGE
114 // In HANDLE.CPP
115 bool ValidHandle(SCNHANDLE offset);
116 
117 // In SCENE.CPP
118 SCNHANDLE GetSceneHandle();
119 #endif
120 
121 //----------------- GLOBAL GLOBAL DATA --------------------
122 
123 // FIXME: Avoid non-const global vars
124 
125 bool g_bEnableMenu;
126 
127 static bool g_bInstantScroll = false;
128 static bool g_bEscapedCdPlay = false;
129 
130 
131 //----------------- LOCAL DEFINES --------------------
132 
133 #define JAP_TEXT_TIME	(2*ONE_SECOND)
134 
135 /*----------------------------------------------------------------------*\
136 |*                      Library Procedure and Function codes            *|
137 \*----------------------------------------------------------------------*/
138 
139 enum MASTER_LIB_CODES {
140 	ACTORATTR, ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY, ACTORREF,
141 	ACTORRGB, ACTORSCALE, ACTORSON, ACTORXPOS, ACTORYPOS, ADDHIGHLIGHT,
142 	ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC, AUXSCALE, BACKGROUND, BLOCKING,
143 	CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT, CALLPROCESS, CALLSCENE, CALLTAG,
144 	CAMERA, CDCHANGESCENE, CDDOCHANGE, CDENDACTOR, CDLOAD, CDPLAY, CLEARHOOKSCENE,
145 	CLOSEINVENTORY, CONTROL, CONVERSATION, CONVTOPIC, CURSOR, CURSORXPOS, CURSORYPOS,
146 	CUTSCENE, DECCONVW, DECCSTRINGS, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
147 	DECLARELANGUAGE, DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELICON,
148 	DELINV, DELTOPIC, DIMMUSIC, DROP, DROPEVERYTHING, DROPOUT, EFFECTACTOR, ENABLEMENU,
149 	ENDACTOR, ESCAPE, ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEMIDI,
150 	FADEOUT, FRAMEGRAB, FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE, HAILSCENE,
151 	HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
152 	HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, ININVENTORY, INSTANTSCROLL, INVDEPICT,
153 	INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLBLOCK, KILLEXIT, KILLGLOBALPROCESS,
154 	KILLPROCESS, KILLTAG, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
155 	NOBLOCKING, NOPAUSE, NOSCROLL, OBJECTHELD, OFFSET, OTHEROBJECT, PAUSE, PLAY, PLAYMIDI,
156 	PLAYMOVIE, PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS,
157 	POSTOBJECT, POSTPROCESS, POSTTAG, PREPARESCENE, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
158 	QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE, RESTORE_CUT,
159 	RESUMELASTGAME, RUNMODE, SAMPLEPLAYING, SAVESCENE, SAY, SAYAT, SCALINGREELS,
160 	SCANICON, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
161 	SENDOBJECT, SENDPROCESS, SENDTAG, SETACTOR, SETBLOCK, SETBRIGHTNESS, SETEXIT, SETINVLIMIT,
162 	SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMREEL, SETSYSTEMSTRING, SETSYSTEMVAR,
163 	SETTAG, SETTIMER, SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWMENU, SHOWPATH,
164 	SHOWPOS, SHOWREFER, SHOWSTRING, SHOWTAG, SPLAY, STAND, STANDTAG, STARTGLOBALPROCESS,
165 	STARTPROCESS, STARTTIMER, STOPMIDI, STOPSAMPLE, STOPWALK, SUBTITLES, SWALK, SWALKZ,
166 	SYSTEMVAR, TAGACTOR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT,
167 	TALKATS, TALKATTR, TALKPALETTEINDEX, TALKRGB, TALKVIA, TEMPTAGFONT, TEMPTALKFONT,
168 	THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
169 	TRYPLAYSAMPLE, UNDIMMUSIC, UNHOOKSCENE, UNTAGACTOR, VIBRATE, WAITFRAME, WAITKEY,
170 	WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
171 	WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY, ZZZZZZ,
172 	HIGHEST_LIBCODE
173 };
174 
175 static const MASTER_LIB_CODES DW1DEMO_CODES[] = {
176 	ACTORREF, ACTORXPOS, ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, AUXSCALE, BACKGROUND,
177 	CAMERA, CONTROL, CONVERSATION, CONVTOPIC, HIGHEST_LIBCODE, CURSORXPOS, CURSORYPOS,
178 	DECCONVW, DECCURSOR, DECTAGFONT, DECINVW, DECINV1, DECINV2, DECLEAD, DELICON,
179 	DELINV, EVENT, HIGHEST_LIBCODE, HELDOBJECT, HIDEACTOR, ININVENTORY, HIGHEST_LIBCODE,
180 	INVENTORY, HIGHEST_LIBCODE, KILLACTOR, KILLBLOCK, KILLTAG, SCREENXPOS,
181 	HIGHEST_LIBCODE, MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, HIGHEST_LIBCODE,
182 	PLAY, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ, PRINTTAG, RESTORESCENE, SAVESCENE,
183 	SCANICON, SCROLL, SETACTOR, SETBLOCK, HIGHEST_LIBCODE, SETTAG, SETTIMER, SHOWPOS,
184 	SPLAY, STAND, STANDTAG, STOPWALK, HIGHEST_LIBCODE, SWALK, TAGACTOR, TALK,
185 	SCREENYPOS, UNTAGACTOR, VIBRATE, WAITKEY, WAITTIME, WALK, WALKINGACTOR, WALKPOLY,
186 	WALKTAG, RANDOM, TIMER
187 };
188 
189 static const MASTER_LIB_CODES DW1_CODES[] = {
190 	ACTORATTR, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS,
191 	ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE,
192 	BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION,
193 	CONVTOPIC, CURSORXPOS, CURSORYPOS, DECCONVW, DECCURSOR,
194 	DECINV1, DECINV2, DECINVW, DECLEAD, DECTAGFONT,
195 	DECTALKFONT, DELICON, DELINV, EFFECTACTOR, ESCAPE, EVENT,
196 	GETINVLIMIT, HELDOBJECT, HIDEACTOR, ININVENTORY, INVDEPICT,
197 	INVENTORY, KILLACTOR, KILLBLOCK, KILLEXIT, KILLTAG, SCREENXPOS,
198 	MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, PAUSE,
199 	PLAY, PLAYMIDI, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ,
200 	PRINTTAG, RANDOM, RESTORESCENE, SAVESCENE, SCALINGREELS,
201 	SCANICON, SCROLL, SETACTOR, SETBLOCK, SETEXIT, SETINVLIMIT,
202 	SETPALETTE, SETTAG, SETTIMER, SHOWPOS, SHOWSTRING, SPLAY,
203 	STAND, STANDTAG, STOPWALK, SWALK, TAGACTOR, TALK, TALKATTR, TIMER,
204 	SCREENYPOS, TOPPLAY, TOPWINDOW, UNTAGACTOR, VIBRATE, WAITKEY,
205 	WAITTIME, WALK, WALKED, WALKINGACTOR, WALKPOLY, WALKTAG,
206 	WHICHINVENTORY, ACTORSON, CUTSCENE, HOOKSCENE, IDLETIME,
207 	RESETIDLETIME, TALKAT, UNHOOKSCENE, WAITFRAME,	DECCSTRINGS,
208 	STOPMIDI, STOPSAMPLE, TALKATS, DECFLAGS, FADEMIDI,
209 	CLEARHOOKSCENE, SETINVSIZE, INWHICHINV, NOBLOCKING,
210 	SAMPLEPLAYING, TRYPLAYSAMPLE, ENABLEMENU, RESTARTGAME, QUITGAME,
211 	FRAMEGRAB, PLAYRTF, CDPLAY, CDLOAD, HASRESTARTED, RESTORE_CUT,
212 	RUNMODE, SUBTITLES, SETLANGUAGE,
213 	HIGHEST_LIBCODE
214 };
215 
216 static const MASTER_LIB_CODES DW2DEMO_CODES[] = {
217 	ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
218 	ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
219 	ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
220 	BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
221 	CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
222 	CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
223 	CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
224 	DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
225 	DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
226 	DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
227 	ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
228 	FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, HASRESTARTED,
229 	HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
230 	HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, INSTANTSCROLL,
231 	INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLGLOBALPROCESS,
232 	KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
233 	NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET, OTHEROBJECT, PAUSE, PLAY,
234 	PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR,
235 	POSTGLOBALPROCESS, POSTOBJECT, POSTPROCESS, POSTTAG, PRINT,
236 	PRINTCURSOR, PRINTOBJ, PRINTTAG, QUITGAME, RANDOM, RESETIDLETIME,
237 	RESTARTGAME, RESTORESCENE, RUNMODE, SAVESCENE, SAY, SAYAT,
238 	SCALINGREELS, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS,
239 	SENDACTOR, SENDGLOBALPROCESS, SENDOBJECT, SENDPROCESS, SENDTAG,
240 	SETBRIGHTNESS, SETINVLIMIT, SETINVSIZE, SETLANGUAGE, SETPALETTE,
241 	SETSYSTEMSTRING, SETSYSTEMVAR, SHELL, SHOWACTOR, SHOWBLOCK,
242 	SHOWEFFECT, SHOWPATH, SHOWREFER, SHOWTAG, STAND, STANDTAG,
243 	STARTGLOBALPROCESS, STARTPROCESS, STARTTIMER, STOPWALK, SUBTITLES,
244 	SWALK, SYSTEMVAR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS,
245 	TALK, TALKAT, TALKPALETTEINDEX, TALKRGB, TALKVIA, THISOBJECT,
246 	THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
247 	UNDIMMUSIC, UNHOOKSCENE, WAITFRAME, WAITKEY, WAITSCROLL, WAITTIME,
248 	WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
249 	WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY,
250 	HIGHEST_LIBCODE
251 };
252 
253 static const MASTER_LIB_CODES DW2_CODES[] = {
254 	ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
255 	ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
256 	ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
257 	BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
258 	CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
259 	CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
260 	CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
261 	DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
262 	DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
263 	DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
264 	ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
265 	FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE,
266 	HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT,
267 	HIDEPATH, HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME,
268 	INSTANTSCROLL, INVENTORY, INVPLAY, INWHICHINV, KILLACTOR,
269 	KILLGLOBALPROCESS, KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG,
270 	MOVETAGTO, NEWSCENE, NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET,
271 	OTHEROBJECT, PAUSE, PLAY, PLAYMUSIC, PLAYRTF, PLAYSAMPLE,
272 	POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS, POSTOBJECT,
273 	POSTPROCESS, POSTTAG, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
274 	QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE,
275 	RUNMODE, SAVESCENE, SAY, SAYAT, SCALINGREELS, SCREENXPOS,
276 	SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
277 	SENDOBJECT, SENDPROCESS, SENDTAG, SETBRIGHTNESS, SETINVLIMIT,
278 	SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMSTRING, SETSYSTEMVAR,
279 	SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWPATH, SHOWREFER,
280 	SHOWTAG, STAND, STANDTAG, STARTGLOBALPROCESS, STARTPROCESS,
281 	STARTTIMER, STOPWALK, SUBTITLES, SWALK, SYSTEMVAR, TAGTAGXPOS,
282 	TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT, TALKPALETTEINDEX,
283 	TALKRGB, TALKVIA, THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY,
284 	TOPWINDOW, TRANSLUCENTINDEX, UNDIMMUSIC, UNHOOKSCENE, WAITFRAME,
285 	WAITKEY, WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG,
286 	WALKINGACTOR, WALKPOLY, WALKTAG, WALKXPOS, WALKYPOS, WHICHCD,
287 	WHICHINVENTORY, ZZZZZZ, SWALKZ, DROPEVERYTHING, BLOCKING, STOPSAMPLE,
288 	CDENDACTOR, DECLARELANGUAGE, RESUMELASTGAME, SHOWMENU, TEMPTALKFONT,
289 	TEMPTAGFONT, PLAYMOVIE, HAILSCENE, SETSYSTEMREEL,
290 	HIGHEST_LIBCODE
291 };
292 
293 //----------------- LOCAL GLOBAL DATA --------------------
294 
295 // FIXME: Avoid non-const global vars
296 
297 // Saved cursor co-ordinates for control(on) to restore cursor position
298 // as it was at control(off).
299 // They are global so that MoveCursor(..) has a net effect if it
300 // precedes control(on).
301 static int g_controlX = 0, g_controlY = 0;
302 
303 static int g_offtype = 0;			// used by Control()
304 static uint32 g_lastValue = 0;	// used by RandomFn()
305 static int g_scrollNumber = 0;	// used by scroll()
306 
307 static bool g_bNotPointedRunning = false;	// Used in Printobj and PrintObjPointed
308 
309 //----------------- FORWARD REFERENCES --------------------
310 
311 static int HeldObject();
312 static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape);
313 void ResetIdleTime();
314 static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result);
315 static void StandTag(int actor, HPOLYGON hp);
316 void StopMidiFn();
317 void StopSample(int sample = -1);
318 static void StopWalk(int actor);
319 static void WaitScroll(CORO_PARAM, int myescEvent);
320 void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath,
321 		  int zOverride, bool escOn, int myescTime);
322 
323 //----------------- SUPPORT FUNCTIONS --------------------
324 
325 /**
326  * For Scroll() and Offset(), work out top left for a
327  * given screen position.
328  */
DecodeExtreme(EXTREME extreme,int * px,int * py)329 static void DecodeExtreme(EXTREME extreme, int *px, int *py) {
330 	int	Loffset, Toffset;
331 
332 	PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
333 
334 	switch (extreme) {
335 	case EX_BOTTOM:
336 		*px = Loffset;
337 		*py = BgHeight() - SCREEN_HEIGHT;
338 		break;
339 	case EX_BOTTOMLEFT:
340 		*px = 0;
341 		*py = BgHeight() - SCREEN_HEIGHT;
342 		break;
343 	case EX_BOTTOMRIGHT:
344 		*px = BgWidth() - SCREEN_WIDTH;
345 		*py = BgHeight() - SCREEN_HEIGHT;
346 		break;
347 	case EX_LEFT:
348 		*px = 0;
349 		*py = Toffset;
350 		break;
351 	case EX_RIGHT:
352 		*px = BgWidth() - SCREEN_WIDTH;
353 		*py = Toffset;
354 		break;
355 	case EX_TOP:
356 		*px = Loffset;
357 		*py = 0;
358 		break;
359 	case EX_TOPLEFT:
360 		*px = *py = 0;
361 		break;
362 	case EX_TOPRIGHT:
363 		*px = BgWidth() - SCREEN_WIDTH;
364 		*py = 0;
365 		break;
366 	default:
367 		break;
368 	}
369 }
370 
KillSelf(CORO_PARAM)371 static void KillSelf(CORO_PARAM) {
372 	CORO_BEGIN_CONTEXT;
373 	CORO_END_CONTEXT(_ctx);
374 
375 	CORO_BEGIN_CODE(_ctx);
376 
377 	CORO_KILL_SELF();
378 
379 	CORO_END_CODE;
380 }
381 
382 struct SCROLL_MONITOR {
383 	int	x;
384 	int	y;
385 	int	thisScroll;
386 	int	myEscape;
387 };
388 typedef SCROLL_MONITOR *PSCROLL_MONITOR;
389 
390 /**
391  * Monitor a scrolling, allowing Escape to interrupt it
392  */
ScrollMonitorProcess(CORO_PARAM,const void * param)393 static void ScrollMonitorProcess(CORO_PARAM, const void *param) {
394 	int		Loffset, Toffset;
395 	const SCROLL_MONITOR *psm = (const SCROLL_MONITOR *)param;
396 
397 	// COROUTINE
398 	CORO_BEGIN_CONTEXT;
399 	CORO_END_CONTEXT(_ctx);
400 
401 	CORO_BEGIN_CODE(_ctx);
402 
403 	do {
404 		CORO_SLEEP(1);
405 
406 		// give up if have been superseded
407 		if (psm->thisScroll != g_scrollNumber)
408 			break;
409 
410 		// If ESCAPE is pressed...
411 		if (psm->myEscape != GetEscEvents()) {
412 			// Instant completion!
413 			Offset(EX_USEXY, psm->x, psm->y);
414 			break;
415 		}
416 
417 		PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
418 
419 	} while (Loffset != psm->x || Toffset != psm->y);
420 
421 	CORO_END_CODE;
422 }
423 
424 /**
425  * NOT A LIBRARY FUNCTION
426  *
427  * Poke supplied color into the DAC queue.
428  */
SetTextPal(COLORREF col)429 void SetTextPal(COLORREF col) {
430 	SetTalkColorRef(col);
431 	UpdateDACqueue(TalkColor(), col);
432 }
433 
434 /**
435  * Work out a time depending on length of string and
436  * subtitle speed modification.
437  */
TextTime(char * pTstring)438 static int TextTime(char *pTstring) {
439 	if (isJapanMode())
440 		return JAP_TEXT_TIME;
441 	else if (!_vm->_config->_textSpeed)
442 		return strlen(pTstring) + ONE_SECOND;
443 	else
444 		return strlen(pTstring) + ONE_SECOND + (_vm->_config->_textSpeed * 5 * ONE_SECOND) / 100;
445 }
446 
447 /**
448  * KeepOnScreen
449  */
KeepOnScreen(POBJECT pText,int * pTextX,int * pTextY)450 void KeepOnScreen(POBJECT pText, int *pTextX, int *pTextY) {
451 	int	shift;
452 
453 	// Not off the left
454 	shift = MultiLeftmost(pText);
455 	if (shift < 0) {
456 		MultiMoveRelXY(pText, - shift, 0);
457 		*pTextX -= shift;
458 	}
459 
460 	// Not off the right
461 	shift = MultiRightmost(pText);
462 	if (shift > SCREEN_WIDTH) {
463 		MultiMoveRelXY(pText, SCREEN_WIDTH - shift, 0);
464 		*pTextX += SCREEN_WIDTH - shift;
465 	}
466 
467 	// Not off the top
468 	shift = MultiHighest(pText);
469 	if (shift < 0) {
470 		MultiMoveRelXY(pText, 0, - shift);
471 		*pTextY -= shift;
472 	}
473 
474 	// Not off the bottom
475 	shift = MultiLowest(pText);
476 	if (shift > SCREEN_BOX_HEIGHT2) {
477 		MultiMoveRelXY(pText, 0, SCREEN_BOX_HEIGHT2 - shift);
478 		*pTextX += SCREEN_WIDTH - shift;
479 	}
480 }
481 
482 /**
483  * Waits until the specified process is finished
484  */
FinishWaiting(CORO_PARAM,const INT_CONTEXT * pic,bool * result=NULL)485 static void FinishWaiting(CORO_PARAM, const INT_CONTEXT *pic, bool *result = NULL) {
486 	CORO_BEGIN_CONTEXT;
487 	CORO_END_CONTEXT(_ctx);
488 
489 	CORO_BEGIN_CODE(_ctx);
490 
491 	while (pic->resumeCode == RES_WAITING)
492 		CORO_SLEEP(1);
493 
494 	if (result)
495 		*result = pic->resumeCode == RES_FINISHED;
496 	CORO_END_CODE;
497 }
498 
TinGetVersion(WHICH_VER which,char * buffer,int length)499 void TinGetVersion(WHICH_VER which, char *buffer, int length) {
500 
501 	if (length > VER_LEN)
502 		length = VER_LEN;
503 
504 	char *cptr = (char *)FindChunk(MASTER_SCNHANDLE, CHUNK_TIME_STAMPS);
505 
506 	switch (which)	{
507 	case VER_GLITTER:
508 		memcpy(buffer, cptr, length);
509 		break;
510 
511 	case VER_COMPILE:
512 		memcpy(buffer, cptr + VER_LEN, length);
513 		break;
514 	}
515 }
516 
517 /********************************************************************\
518 |*****			Library functions								*****|
519 \********************************************************************/
520 
521 /**
522  * Set actor's attributes.
523  * - currently only the text color.
524  */
ActorAttr(int actor,int r1,int g1,int b1)525 static void ActorAttr(int actor, int r1, int g1, int b1) {
526 	storeActorAttr(actor, r1, g1, b1);
527 }
528 
529 /**
530  * Behave as if actor has walked into a polygon with given brughtness.
531  */
ActorBrightness(int actor,int brightness)532 void ActorBrightness(int actor, int brightness) {
533 	PMOVER pMover = GetMover(actor);
534 
535 	assert(pMover != NULL);
536 	assert(brightness >= 0 && brightness <= 10);
537 
538 	MoverBrightness(pMover, brightness);
539 }
540 
541 /**
542  * Return a moving actor's current direction.
543  */
ActorDirection(int actor)544 static int ActorDirection(int actor) {
545 	PMOVER pMover = GetMover(actor);
546 	assert(pMover);
547 
548 	return (int)GetMoverDirection(pMover);
549 }
550 
551 /**
552  * Set actor's palette details for path brightnesses
553  */
ActorPalette(int actor,int startColor,int length)554 void ActorPalette(int actor, int startColor, int length) {
555 	PMOVER pMover = GetMover(actor);
556 	assert(pMover);
557 
558 	StoreMoverPalette(pMover, startColor, length);
559 }
560 
561 /**
562  * Set actor's Z-factor.
563  */
ActorPriority(int actor,int zFactor)564 static void ActorPriority(int actor, int zFactor) {
565 	SetActorZfactor(actor, zFactor);
566 }
567 
568 /**
569  * Set actor's text color.
570  */
ActorRGB(int actor,COLORREF color)571 static void ActorRGB(int actor, COLORREF color) {
572 	SetActorRGB(actor, color);
573 }
574 
575 /**
576  * Return the actor's scale.
577  */
ActorScale(int actor)578 static int ActorScale(int actor) {
579 	PMOVER pMover = GetMover(actor);
580 	assert(pMover);
581 
582 	return (int)GetMoverScale(pMover);
583 }
584 
585 /**
586  * Returns the x or y position of an actor.
587  */
ActorPos(int xory,int actor)588 static int ActorPos(int xory, int actor) {
589 	int x, y;
590 
591 	GetActorPos(actor, &x, &y);
592 	return (xory == ACTORXPOS) ? x : y;
593 }
594 
595 /**
596  * Make all actors alive at the start of each scene.
597  */
ActorsOn()598 static void ActorsOn() {
599 	setactorson();
600 }
601 
602 /**
603  * Adds an icon to the conversation window.
604  */
AddTopic(int icon)605 static void AddTopic(int icon) {
606 	AddToInventory(INV_CONV, icon, false);
607 }
608 
609 /**
610  * Place the object in inventory 1 or 2.
611  */
AddInv(int invno,int object)612 static void AddInv(int invno, int object) {
613 	// illegal inventory number
614 	assert(invno == INV_1 || invno == INV_2 || invno == INV_OPEN || invno == INV_DEFAULT);
615 
616 	AddToInventory(invno, object, false);
617 }
618 
619 /**
620  * Define an actor's walk and stand reels for an auxilliary scale.
621  */
AuxScale(int actor,int scale,SCNHANDLE * rp)622 static void AuxScale(int actor, int scale, SCNHANDLE *rp) {
623 	PMOVER pMover = GetMover(actor);
624 	assert(pMover);
625 
626 	int j;
627 	for (j = 0; j < 4; ++j)
628 		pMover->walkReels[scale-1][j] = *rp++;
629 	for (j = 0; j < 4; ++j)
630 		pMover->standReels[scale-1][j] = *rp++;
631 	for (j = 0; j < 4; ++j)
632 		pMover->talkReels[scale-1][j] = *rp++;
633 }
634 
635 /**
636  * Defines the background image for a scene.
637  */
Background(CORO_PARAM,SCNHANDLE bfilm)638 static void Background(CORO_PARAM, SCNHANDLE bfilm) {
639 	StartupBackground(coroParam, bfilm);
640 }
641 
642 /**
643  * Disable dynamic blocking for current scene.
644  */
Blocking(bool onOrOff)645 void Blocking(bool onOrOff) {
646 	SetSysVar(ISV_NO_BLOCKING, !onOrOff);
647 }
648 
649 /**
650  * Sets focus of the scroll process.
651  */
Camera(int actor)652 static void Camera(int actor) {
653 	ScrollFocus(actor);
654 }
655 
656 /**
657  * Sets the CD Change Scene
658  */
659 
CdChangeScene(SCNHANDLE hScene)660 static void CdChangeScene(SCNHANDLE hScene) {
661 	SetCdChangeScene(hScene);
662 }
663 
664 /**
665  * CdDoChange
666  */
CdDoChange(CORO_PARAM)667 void CdDoChange(CORO_PARAM) {
668 	CORO_BEGIN_CONTEXT;
669 	CORO_END_CONTEXT(_ctx);
670 
671 	CORO_BEGIN_CODE(_ctx);
672 
673 	if (!GotoCD())
674 		return;
675 
676 	CORO_INVOKE_0(CdCD);
677 
678 	CdHasChanged();
679 
680 	CORO_END_CODE;
681 }
682 
683 /**
684  * CdEndActor("actor")
685  */
CdEndActor(int actor,int myEscape)686 void CdEndActor(int	actor, int	myEscape) {
687 	PMOVER	pMover;			// for if it's a moving actor
688 
689 	// Only do it if escaped!
690 	if (myEscape && myEscape != GetEscEvents()) {
691 		// End current graphic
692 		dwEndActor(actor);
693 
694 		// un-hide movers
695 		pMover = GetMover(actor);
696 		if (pMover)
697 			UnHideMover(pMover);
698 	}
699 }
700 
701 /**
702  * A CDPLAY() is imminent.
703  */
CDload(SCNHANDLE start,SCNHANDLE next,int myEscape)704 static void CDload(SCNHANDLE start, SCNHANDLE next, int myEscape) {
705 	assert(start && next && start != next); // cdload() fault
706 
707 	if (TinselV2) {
708 		if (myEscape && myEscape != GetEscEvents()) {
709 			g_bEscapedCdPlay = true;
710 			return;
711 		}
712 
713 		LoadExtraGraphData(start, next);
714 	}
715 }
716 
717 /**
718  * Clear the hooked scene (if any)
719  */
ClearHookScene()720 static void ClearHookScene() {
721 	SetHookScene(0, 0, TRANS_DEF);
722 }
723 
724 /**
725  * Guess what.
726  */
CloseInventory()727 static void CloseInventory() {
728 	KillInventory();
729 }
730 
731 /**
732  * Turn off cursor and take control from player - and variations on the	 theme.
733  *  OR Restore cursor and return control to the player.
734  */
Control(int param)735 void Control(int param) {
736 	if (TinselV2) {
737 		if (param)
738 			ControlOn();
739 		else {
740 			ControlOff();
741 
742 			switch (WhichInventoryOpen()) {
743 			case INV_1:
744 			case INV_2:
745 			case INV_MENU:
746 				KillInventory();
747 				break;
748 			default:
749 				break;
750 			}
751 		}
752 
753 		return;
754 	}
755 
756 	// Tinsel 1 handling code
757 	g_bEnableMenu = false;
758 
759 	switch (param) {
760 	case CONTROL_STARTOFF:
761 		GetControlToken();	// Take control
762 		DisableTags();			// Switch off tags
763 		DwHideCursor();			// Blank out cursor
764 		g_offtype = param;
765 		break;
766 
767 	case CONTROL_OFF:
768 	case CONTROL_OFFV:
769 	case CONTROL_OFFV2:
770 		if (TestToken(TOKEN_CONTROL)) {
771 			GetControlToken();	// Take control
772 
773 			DisableTags();			// Switch off tags
774 			GetCursorXYNoWait(&g_controlX, &g_controlY, true);	// Store cursor position
775 
776 			// There may be a button timing out
777 			GetToken(TOKEN_LEFT_BUT);
778 			FreeToken(TOKEN_LEFT_BUT);
779 		}
780 
781 		if (g_offtype == CONTROL_STARTOFF)
782 			GetCursorXYNoWait(&g_controlX, &g_controlY, true);	// Store cursor position
783 
784 		g_offtype = param;
785 
786 		if (param == CONTROL_OFF)
787 			DwHideCursor();		// Blank out cursor
788 		else if (param == CONTROL_OFFV) {
789 			UnHideCursor();
790 			FreezeCursor();
791 		} else if (param == CONTROL_OFFV2) {
792 			UnHideCursor();
793 		}
794 		break;
795 
796 	case CONTROL_ON:
797 		if (g_offtype != CONTROL_OFFV2 && g_offtype != CONTROL_STARTOFF)
798 			SetCursorXY(g_controlX, g_controlY);// ... where it was
799 
800 		FreeControlToken();	// Release control
801 
802 		if (!InventoryActive())
803 			EnableTags();		// Tags back on
804 
805 		RestoreMainCursor();		// Re-instate cursor...
806 	}
807 }
808 
809 /**
810  * Open or close the conversation window.
811  */
Conversation(CORO_PARAM,int fn,HPOLYGON hp,int actor,bool escOn,int myEscape)812 static void Conversation(CORO_PARAM, int fn, HPOLYGON hp, int actor, bool escOn, int myEscape) {
813 	assert(hp != NOPOLY); // conversation() must (currently) be called from a polygon code block
814 	CORO_BEGIN_CONTEXT;
815 	CORO_END_CONTEXT(_ctx);
816 
817 	CORO_BEGIN_CODE(_ctx);
818 
819 	if (fn == CONV_END) {
820 		// Close down conversation
821 		CloseDownConv();
822 	} else if ((fn == CONV_TOP) || (fn == CONV_DEF) || (fn == CONV_BOTTOM)) {
823 		// TOP of screen, Default (i.e. TOP of screen), or BOTTOM of screen
824 
825 		// If waiting is enabled, wait for ongoing scroll
826 		if (TinselV2 && SysVar(SV_CONVERSATIONWAITS))
827 			CORO_INVOKE_1(WaitScroll, myEscape);
828 
829 		// Don't do it if it's not wanted
830 		if (escOn && myEscape != GetEscEvents())
831 			return;
832 
833 		// Don't do it if already in a conversation
834 		if (IsConvWindow())
835 			return;
836 
837 		KillInventory();
838 
839 		if (TinselV2) {
840 			// If this is from a tag polygon, get the associated
841 			// actor (the one the polygon is named after), if any.
842 			if (!actor) {
843 				actor = GetTagPolyId(hp);
844 				if (actor & ACTORTAG_KEY)
845 					actor &= ~ACTORTAG_KEY;
846 				else
847 					actor = 0;
848 			}
849 
850 			// Top or bottom; tag polygon or tagged actor
851 			SetConvDetails((CONV_PARAM)fn, hp, actor);
852 		} else {
853 			convPos(fn);
854 			ConvPoly(hp);
855 		}
856 
857 		PopUpInventory(INV_CONV);	// Conversation window
858 		ConvAction(INV_OPENICON);	// CONVERSATION event
859 	}
860 
861 	CORO_END_CODE;
862 }
863 
864 /**
865  * Add icon to conversation window's permanent default list.
866  */
ConvTopic(int icon)867 static void ConvTopic(int icon) {
868 	PermaConvIcon(icon);
869 }
870 
871 /**
872  * Cursor(on/off)
873  */
Cursor(int onoff)874 void Cursor(int onoff) {
875 	if (onoff) {
876 		// Re-instate cursor
877 		UnHideCursor();
878 	} else {
879 		// Blank out cursor
880 		DwHideCursor();
881 	}
882 }
883 
884 /**
885  * Returns the x or y position of the cursor.
886  */
CursorPos(int xory)887 static int CursorPos(int xory) {
888 	int x, y;
889 
890 	GetCursorXY(&x, &y, true);
891 	return (xory == CURSORXPOS) ? x : y;
892 }
893 
894 /**
895  * Declare conversation window.
896  */
DecConvW(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)897 static void DecConvW(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
898 			int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
899 	idec_convw(text, MaxContents, MinWidth, MinHeight,
900 			StartWidth, StartHeight, MaxWidth, MaxHeight);
901 }
902 
903 /**
904  * Declare config strings.
905  */
DecCStrings(SCNHANDLE * tp)906 static void DecCStrings(SCNHANDLE *tp) {
907 	setConfigStrings(tp);
908 }
909 
910 /**
911  * Declare cursor's reels.
912  */
DecCursor(SCNHANDLE hFilm)913 static void DecCursor(SCNHANDLE hFilm) {
914 	DwInitCursor(hFilm);
915 }
916 
917 /**
918  * Declare the language flags.
919  */
DecFlags(SCNHANDLE hFilm)920 static void DecFlags(SCNHANDLE hFilm) {
921 	setFlagFilms(hFilm);
922 }
923 
924 /**
925  * Declare inventory 1's parameters.
926  */
DecInv1(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)927 static void DecInv1(SCNHANDLE text, int MaxContents,
928 		int MinWidth, int MinHeight,
929 		int StartWidth, int StartHeight,
930 		int MaxWidth, int MaxHeight) {
931 	idec_inv1(text, MaxContents, MinWidth, MinHeight,
932 			StartWidth, StartHeight, MaxWidth, MaxHeight);
933 }
934 
935 /**
936  * Declare inventory 2's parameters.
937  */
DecInv2(SCNHANDLE text,int MaxContents,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)938 static void DecInv2(SCNHANDLE text, int MaxContents,
939 		int MinWidth, int MinHeight,
940 		int StartWidth, int StartHeight,
941 		int MaxWidth, int MaxHeight) {
942 	idec_inv2(text, MaxContents, MinWidth, MinHeight,
943 			StartWidth, StartHeight, MaxWidth, MaxHeight);
944 }
945 
946 /**
947  * Declare the bits that the inventory windows are constructed from.
948  */
DecInvW(SCNHANDLE hf)949 static void DecInvW(SCNHANDLE hf) {
950 	setInvWinParts(hf);
951 }
952 
953 /**
954  * DeclareLanguage
955  */
DeclareLanguage(int languageId,SCNHANDLE hDescription,SCNHANDLE hFlagFilm)956 static void DeclareLanguage(int languageId, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
957 	LanguageFacts(languageId, hDescription, hFlagFilm);
958 }
959 
960 /**
961  * Declare lead actor.
962  * @param id		Actor Id
963  * @param rp		Walk and stand reels for all the regular scales (v1 only)
964  * @param text		Tag text (v1 only)
965  */
DecLead(uint32 id,SCNHANDLE * rp=0,SCNHANDLE text=0)966 static void DecLead(uint32 id, SCNHANDLE *rp = 0, SCNHANDLE text = 0) {
967 	PMOVER	pMover;		// Moving actor structure
968 
969 	if (TinselV2) {
970 		// Tinsel 2 only specifies the lead actor Id
971 		SetLeadId(id);
972 		RegisterMover(id);
973 
974 	} else {
975 
976 		Tag_Actor(id, text, TAG_DEF);	// The lead actor is automatically tagged
977 		SetLeadId(id);			// Establish this as the lead
978 		RegisterMover(id);			// Establish as a moving actor
979 
980 		pMover = GetMover(id);		// Get moving actor structure
981 		assert(pMover);
982 
983 		// Store all those reels
984 		int i, j;
985 		for (i = 0; i < 5; ++i) {
986 			for (j = 0; j < 4; ++j)
987 				pMover->walkReels[i][j] = *rp++;
988 			for (j = 0; j < 4; ++j)
989 				pMover->standReels[i][j] = *rp++;
990 			for (j = 0; j < 4; ++j)
991 				pMover->talkReels[i][j] = *rp++;
992 		}
993 
994 
995 		for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
996 			for (j = 0; j < 4; ++j) {
997 				pMover->walkReels[i][j] = pMover->walkReels[4][j];
998 				pMover->standReels[i][j] = pMover->standReels[2][j];
999 				pMover->talkReels[i][j] = pMover->talkReels[4][j];
1000 			}
1001 		}
1002 	}
1003 }
1004 
1005 /**
1006  * DecScale("actor", scale, 12*"reel")
1007  * Define an actor's walk and stand reels for a scale.
1008  */
DecScale(int actor,int scale,SCNHANDLE wkl,SCNHANDLE wkr,SCNHANDLE wkf,SCNHANDLE wka,SCNHANDLE stl,SCNHANDLE str,SCNHANDLE stf,SCNHANDLE sta,SCNHANDLE tal,SCNHANDLE tar,SCNHANDLE taf,SCNHANDLE taa)1009 static void DecScale(int actor, int scale,
1010 		SCNHANDLE wkl, SCNHANDLE wkr, SCNHANDLE wkf, SCNHANDLE wka,
1011 		SCNHANDLE stl, SCNHANDLE str, SCNHANDLE stf, SCNHANDLE sta,
1012 		SCNHANDLE tal, SCNHANDLE tar, SCNHANDLE taf, SCNHANDLE taa) {
1013 	PMOVER pMover = GetMover(actor);
1014 	assert(pMover);
1015 
1016 	SetWalkReels(pMover, scale, wkl, wkr, wkf, wka);
1017 	SetStandReels(pMover, scale, stl, str, stf, sta);
1018 	SetTalkReels(pMover, scale, tal, tar, taf, taa);
1019 }
1020 
1021 /**
1022  * Declare the text font.
1023  */
DecTagFont(SCNHANDLE hf)1024 static void DecTagFont(SCNHANDLE hf) {
1025 	SetTagFontHandle(hf);		// Store the font handle
1026 	if (TinselV0)
1027 		SetTalkFontHandle(hf);	// Also re-use for talk text
1028 }
1029 
1030 /**
1031  * Declare the text font.
1032  */
DecTalkFont(SCNHANDLE hf)1033 static void DecTalkFont(SCNHANDLE hf) {
1034 	SetTalkFontHandle(hf);		// Store the font handle
1035 }
1036 
1037 /**
1038  * Remove an icon from the conversation window.
1039  */
DelIcon(int icon)1040 static void DelIcon(int icon) {
1041 	RemFromInventory(INV_CONV, icon);
1042 }
1043 
1044 /**
1045  * Delete the object from inventory 1 or 2.
1046  */
DelInv(int object)1047 static void DelInv(int object) {
1048 	if (!RemFromInventory(INV_1, object))		// Remove from inventory 1...
1049 		RemFromInventory(INV_2, object);		// ...or 2 (whichever)
1050 
1051 	DropItem(object);			// Stop holding it
1052 }
1053 
1054 /**
1055  * DelTopic
1056  */
DelTopic(int icon)1057 static void DelTopic(int icon) {
1058 	RemFromInventory(INV_CONV, icon);
1059 }
1060 
1061 /**
1062  * DimMusic
1063  */
DimMusic()1064 static void DimMusic() {
1065 	_vm->_pcmMusic->dim(true);
1066 }
1067 
1068 /**
1069  * Delete the object from inventory 1 or 2.
1070  */
Drop(int object)1071 static void Drop(int object) {
1072 	if (object == -1)
1073 		object = HeldObject();
1074 
1075 	if (!RemFromInventory(INV_1, object))	// Remove from inventory 1...
1076 		RemFromInventory(INV_2, object);	// ...or 2 (whichever)
1077 
1078 	DropItem(object);			// Stop holding it
1079 }
1080 
1081 /**
1082  * Delete all objects from inventory 1 and 2.
1083  */
DropEverything()1084 static void DropEverything() {
1085 	HoldItem(NOOBJECT, false);
1086 
1087 	ClearInventory(INV_1);
1088 	ClearInventory(INV_2);
1089 }
1090 
1091 /**
1092  * EnableMenu
1093  */
EnableMenu()1094 static void EnableMenu() {
1095 	g_bEnableMenu = true;
1096 }
1097 
1098 /**
1099  * Kill an actor's current graphics.
1100  */
EndActor(int actor)1101 static void EndActor(int actor) {
1102 	dwEndActor(actor);
1103 }
1104 
1105 /**
1106  * Get the actor to look at the polygon.
1107  * If the actor is at the tag, do a StandTag().
1108  */
FaceTag(int actor,HPOLYGON hp)1109 static void FaceTag(int actor, HPOLYGON hp) {
1110 	PMOVER	pMover;		// Moving actor structure
1111 	int	nowx, nowy;
1112 	int	nodex, nodey;
1113 
1114 	assert(hp != NOPOLY);
1115 
1116 	/*
1117 	 * Get which moving actor it is
1118 	 */
1119 	pMover = GetMover(actor);
1120 	assert(pMover);
1121 	if (MoverHidden(pMover))
1122 		return;
1123 
1124 	/*
1125 	 * Stop the actor
1126 	 */
1127 	StopWalk(actor);
1128 
1129 	/*
1130 	 * Face the tag
1131 	 */
1132 	// See where node is and where actor is
1133 	GetPolyNode(hp, &nodex, &nodey);
1134 	GetActorPos(actor, &nowx, &nowy);
1135 
1136 	if (nowx == nodex && nowy == nodey) {
1137 		// Stood at the tag, don't face in silly direction
1138 		StandTag(actor, hp);
1139 	} else {
1140 		// Look towards polygon
1141 		GetPolyMidBottom(hp, &nodex, &nodey);
1142 		SetMoverDirection(pMover, GetDirection(nowx, nowy,
1143 						nodex, nodey,
1144 						GetMoverDirection(pMover),
1145 						NOPOLY, YB_X1_5));
1146 		SetMoverStanding(pMover);
1147 	}
1148 }
1149 
1150 /**
1151  * FadeIn
1152  */
FadeIn()1153 static void FadeIn() {
1154 	FadeInMedium();
1155 }
1156 
1157 /**
1158  * FadeOut
1159  */
FadeOut()1160 static void FadeOut() {
1161 	FadeOutMedium();
1162 }
1163 
1164 /**
1165  * FadeMidi(in/out)
1166  */
FadeMidi(CORO_PARAM,int inout)1167 static void FadeMidi(CORO_PARAM, int inout) {
1168 	CORO_BEGIN_CONTEXT;
1169 	CORO_END_CONTEXT(_ctx);
1170 
1171 	CORO_BEGIN_CODE(_ctx);
1172 	assert(inout == FM_IN || inout == FM_OUT);
1173 
1174 	// To prevent compiler complaining
1175 	if (inout == FM_IN || inout == FM_OUT)
1176 		CORO_SLEEP(1);
1177 	CORO_END_CODE;
1178 }
1179 
1180 /**
1181  * Freeze the cursor, or not.
1182  */
FreezeCursor(bool bFreeze)1183 static void FreezeCursor(bool bFreeze) {
1184 	DoFreezeCursor(bFreeze);
1185 }
1186 
1187 /**
1188  * Guess what.
1189  */
GetInvLimit(int invno)1190 static int GetInvLimit(int invno) {
1191 	return InvGetLimit(invno);
1192 }
1193 
1194 /**
1195  * Ghost
1196  */
Ghost(int actor,int tColor,int tPalOffset)1197 static void Ghost(int actor, int tColor, int tPalOffset) {
1198 	SetSysVar(ISV_GHOST_ACTOR, actor);
1199 	SetSysVar(ISV_GHOST_COLOR,  tColor);
1200 	SetSysVar(ISV_GHOST_BASE, tPalOffset);
1201 }
1202 
1203 /**
1204  *
1205  */
HailScene(SCNHANDLE scene)1206 static void HailScene(SCNHANDLE scene) {
1207 	DoHailScene(scene);
1208 }
1209 
1210 /**
1211  * Returns TRUE if the game has been restarted, FALSE if not.
1212  */
HasRestarted()1213 static bool HasRestarted() {
1214 	return g_bHasRestarted;
1215 }
1216 
1217 /**
1218  * See if an object is in the inventory.
1219  */
Have(int object)1220 int Have(int object) {
1221 	return (InventoryPos(object) != NOOBJECT);
1222 }
1223 
1224 /**
1225  * Returns which object is currently held.
1226  */
HeldObject()1227 static int HeldObject() {
1228 	return WhichItemHeld();
1229 }
1230 
1231 /**
1232  * Hides the specified actor
1233  */
HideActorFn(CORO_PARAM,int ano)1234 static void HideActorFn(CORO_PARAM, int ano) {
1235 	HideActor(coroParam, ano);
1236 }
1237 
1238 /**
1239  * Turn a blocking polygon off.
1240  */
HideBlock(int block)1241 static void HideBlock(int block) {
1242 	DisableBlock(block);
1243 }
1244 
1245 /**
1246  * Turn an effect polygon off.
1247  */
HideEffect(int effect)1248 static void HideEffect(int effect) {
1249 	DisableEffect(effect);
1250 }
1251 
1252 /**
1253  * Turn a path polygon off.
1254  */
HidePath(int path)1255 static void HidePath(int path) {
1256 	DisablePath(path);
1257 }
1258 
1259 /**
1260  * Turn a refer polygon off.
1261  */
HideRefer(int refer)1262 static void HideRefer(int refer) {
1263 	DisableRefer(refer);
1264 }
1265 
1266 /**
1267  * Turn a tag polygon off.
1268  */
HideTag(CORO_PARAM,int tag,HPOLYGON hp)1269 static void HideTag(CORO_PARAM, int tag, HPOLYGON hp) {
1270 	// Tag could be zero, meaning calling tag
1271 	DisableTag(coroParam, tag ? tag : GetTagPolyId(hp));
1272 }
1273 
1274 /**
1275  * Hold the specified object.
1276  */
Hold(int object)1277 static void Hold(int object) {
1278 	HoldItem(object, false);
1279 }
1280 
1281 /**
1282  * HookScene(scene, entrance, transition)
1283  */
HookScene(SCNHANDLE scene,int entrance,int transition)1284 void HookScene(SCNHANDLE scene, int entrance, int transition) {
1285 	SetHookScene(scene, entrance, transition);
1286 }
1287 
1288 /**
1289  * IdleTime
1290  */
IdleTime()1291 static int IdleTime() {
1292 	// If control is off, system is not idle
1293 	if (!ControlIsOn()) {
1294 		// Player doesn't currently have control
1295 		ResetIdleTime();
1296 
1297 		return 0;
1298 	} else {
1299 		// Player has control - return time since last event
1300 		int x = getUserEventTime() / ONE_SECOND;
1301 
1302 		return x;
1303 	}
1304 }
1305 
1306 /**
1307  * Set flag if InstantScroll(on), reset if InstantScroll(off)
1308  */
InstantScroll(int onoff)1309 void InstantScroll(int onoff) {
1310 	g_bInstantScroll = (onoff != 0);
1311 }
1312 
1313 /**
1314  * invdepict
1315  */
InvDepict(int object,SCNHANDLE hFilm)1316 static void InvDepict(int object, SCNHANDLE hFilm) {
1317 	SetObjectFilm(object, hFilm);
1318 }
1319 
1320 /**
1321  * See if an object is in the inventory.
1322  */
InInventory(int object)1323 int InInventory(int object) {
1324 	return (InventoryPos(object) != INV_NOICON);
1325 }
1326 
1327 /**
1328  * Open an inventory.
1329  */
Inventory(int invno,bool escOn,int myEscape)1330 static void Inventory(int invno, bool escOn, int myEscape) {
1331 	// Don't do it if it's not wanted
1332 	if (escOn && myEscape != GetEscEvents())
1333 		return;
1334 
1335 	assert((invno == INV_1 || invno == INV_2)); // Trying to open illegal inventory
1336 
1337 	PopUpInventory(invno);
1338 }
1339 
1340 /**
1341  * Alter inventory object's icon.
1342  */
InvPlay(int object,SCNHANDLE hFilm)1343 static void InvPlay(int object, SCNHANDLE hFilm) {
1344 	SetObjectFilm(object, hFilm);
1345 }
1346 
1347 /**
1348  * See if an object is in the inventory.
1349  */
InWhichInv(int object)1350 static int InWhichInv(int object) {
1351 	if (WhichItemHeld() == object)
1352 		return 0;
1353 
1354 	if (IsInInventory(object, INV_1))
1355 		return 1;
1356 
1357 	if (IsInInventory(object, INV_2))
1358 		return 2;
1359 
1360 	return -1;
1361 }
1362 
1363 /**
1364  * Kill an actor.
1365  */
KillActor(int actor)1366 static void KillActor(int actor) {
1367 	DisableActor(actor);
1368 }
1369 
1370 /**
1371  * Turn a blocking polygon off.
1372  */
KillBlock(int block)1373 static void KillBlock(int block) {
1374 	DisableBlock(block);
1375 }
1376 
1377 /**
1378  * Turn an exit off.
1379  */
KillExit(int exit)1380 static void KillExit(int exit) {
1381 	DisableExit(exit);
1382 }
1383 
1384 /**
1385  * Turn a tag off.
1386  */
KillTag(CORO_PARAM,int tagno)1387 static void KillTag(CORO_PARAM, int tagno) {
1388 	DisableTag(coroParam, tagno);
1389 }
1390 
1391 /**
1392  * Kills the specified global process
1393  */
KillGlobalProcess(uint32 procID)1394 static void KillGlobalProcess(uint32 procID) {
1395 	xKillGlobalProcess(procID);
1396 }
1397 
1398 /**
1399  * Kills the specified process
1400  */
KillProcess(uint32 procID)1401 static void KillProcess(uint32 procID) {
1402 	KillSceneProcess(procID);
1403 }
1404 
1405 /**
1406  * Returns the left or top offset of the screen.
1407  */
LToffset(int lort)1408 static int LToffset(int lort) {
1409 	int Loffset, Toffset;
1410 
1411 	PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
1412 	return (lort == SCREENXPOS) ? Loffset : Toffset;
1413 }
1414 
1415 /**
1416  * Set new cursor position.
1417  */
MoveCursor(int x,int y)1418 static void MoveCursor(int x, int y) {
1419 	SetCursorXY(x, y);
1420 
1421 	g_controlX = x;		// Save these values so that
1422 	g_controlY = y;		// control(on) doesn't undo this
1423 }
1424 
1425 /**
1426  * MoveTag(tag, x, y)
1427  */
MoveTag(int tag,int x,int y,HPOLYGON hp)1428 void MoveTag(int tag, int x, int y, HPOLYGON hp) {
1429 	// Tag could be zero, meaning calling tag
1430 	MovePolygon(TAG, tag ? tag : GetTagPolyId(hp), x, y);
1431 }
1432 
1433 /**
1434  * MoveTagTo(tag, x, y)
1435  */
MoveTagTo(int tag,int x,int y,HPOLYGON hp)1436 void MoveTagTo(int tag, int x, int y, HPOLYGON hp) {
1437 	// Tag could be zero, meaning calling tag
1438 	MovePolygonTo(TAG, tag ? tag : GetTagPolyId(hp), x, y);
1439 }
1440 
1441 /**
1442  * Triggers change to a new scene.
1443  */
NewScene(CORO_PARAM,SCNHANDLE scene,int entrance,int transition)1444 void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
1445 	// COROUTINE
1446 	CORO_BEGIN_CONTEXT;
1447 	CORO_END_CONTEXT(_ctx);
1448 
1449 	CORO_BEGIN_CODE(_ctx);
1450 
1451 	if (TinselV2) {
1452 		if (_vm->_bmv->MoviePlaying()) {
1453 			_vm->_bmv->AbortMovie();
1454 			CORO_SLEEP(2);
1455 		}
1456 	}
1457 
1458 	SetNewScene(scene, entrance, transition);
1459 
1460 	// Prevent tags and cursor re-appearing
1461 	if (TinselV2)
1462 		ControlStartOff();
1463 	else
1464 		GetControl(CONTROL_STARTOFF);
1465 
1466 	if (TinselV1)
1467 		++g_sceneCtr;
1468 
1469 	// Prevent code subsequent to this call running before scene changes
1470 	if (CoroScheduler.getCurrentPID() != PID_MASTER_SCR)
1471 		CORO_KILL_SELF();
1472 	CORO_END_CODE;
1473 }
1474 
1475 /**
1476  * Disable dynamic blocking for current scene.
1477  */
NoBlocking()1478 static void NoBlocking() {
1479 	SetNoBlocking(true);
1480 }
1481 
1482 /**
1483  * Define a no-scroll boundary for the current scene.
1484  */
NoScroll(int x1,int y1,int x2,int y2)1485 static void NoScroll(int x1, int y1, int x2, int y2) {
1486 	SetNoScroll(x1, y1, x2, y2);
1487 }
1488 
1489 /**
1490  * Hold the specified object.
1491  */
ObjectHeld(int object)1492 static void ObjectHeld(int object) {
1493 	HoldItem(object);
1494 }
1495 
1496 /**
1497  * Set the top left offset of the screen.
1498  */
Offset(EXTREME extreme,int x,int y)1499 void Offset(EXTREME extreme, int x, int y) {
1500 	KillScroll();
1501 
1502 	if (TinselV2)
1503 		DecodeExtreme(extreme, &x, &y);
1504 
1505 	PlayfieldSetPos(FIELD_WORLD, x, y);
1506 }
1507 
1508 /**
1509  * OtherObject()
1510  */
OtherObject(INV_OBJECT * pinvo)1511 int OtherObject(INV_OBJECT *pinvo) {
1512 	assert(pinvo != NULL);
1513 
1514 	// return held object or object clicked on - whichever is not the calling object
1515 
1516 	// pinvo->id is the calling object
1517 	// WhichItemHeld() gives the held object
1518 	// GetIcon() gives the object clicked on
1519 
1520 	assert(GetIcon() == pinvo->id || WhichItemHeld() == pinvo->id);
1521 
1522 	if (GetIcon() == pinvo->id)
1523 		return WhichItemHeld();
1524 	else
1525 		return GetIcon();
1526 }
1527 
1528 /**
1529  * Play a film.
1530  */
Play(CORO_PARAM,SCNHANDLE hFilm,int x,int y,int compit,int actorid,bool splay,int sfact,bool escOn,int myEscape,bool bTop)1531 static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int compit, int actorid, bool splay, int sfact,
1532 		  bool escOn, int myEscape, bool bTop) {
1533 	assert(hFilm != 0); // play(): Trying to play NULL film
1534 
1535 	// COROUTINE
1536 	CORO_BEGIN_CONTEXT;
1537 	CORO_END_CONTEXT(_ctx);
1538 
1539 	CORO_BEGIN_CODE(_ctx);
1540 
1541 
1542 	// Don't do CDPlay() for now if already escaped
1543 	if (g_bEscapedCdPlay) {
1544 		g_bEscapedCdPlay = false;
1545 		return;
1546 	}
1547 
1548 	// Don't do it if it's not wanted
1549 	if (escOn && myEscape != GetEscEvents())
1550 		return;
1551 
1552 	// If this actor is dead, call a stop to the calling process
1553 	if (actorid && !actorAlive(actorid))
1554 		CORO_KILL_SELF();
1555 
1556 	// 7/4/95
1557 	if (!escOn)
1558 		myEscape = GetEscEvents();
1559 
1560 	if (compit == 1) {
1561 		// Play to completion before returning
1562 		CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
1563 	} else if (compit == 2) {
1564 		error("play(): compit == 2 - please advise John");
1565 	} else {
1566 		// Kick off the play and return.
1567 		CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
1568 	}
1569 	CORO_END_CODE;
1570 }
1571 
1572 /**
1573  * Play a film
1574  */
Play(CORO_PARAM,SCNHANDLE hFilm,int x,int y,bool bComplete,int myEscape,bool bTop,TINSEL_EVENT event,HPOLYGON hPoly,int taggedActor)1575 static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myEscape,
1576 		bool bTop, TINSEL_EVENT event, HPOLYGON hPoly, int taggedActor) {
1577 	CORO_BEGIN_CONTEXT;
1578 	CORO_END_CONTEXT(_ctx);
1579 
1580 	CORO_BEGIN_CODE(_ctx);
1581 
1582 	assert(hFilm != 0);
1583 
1584 	// Don't do CdPlay() for now if already escaped
1585 	if (g_bEscapedCdPlay) {
1586 		g_bEscapedCdPlay = false;
1587 		return;
1588 	}
1589 
1590 	if (event == TALKING) {
1591 		int	actor;
1592 		if (hPoly == NOPOLY) {
1593 			// Must be a tagged actor
1594 
1595 			assert(taggedActor && IsTaggedActor(taggedActor));
1596 			actor = taggedActor;
1597 		} else if (taggedActor == 0) {
1598 			// Must be a polygon with an actor ID
1599 			actor = GetTagPolyId(hPoly);
1600 			assert(actor & ACTORTAG_KEY);
1601 			actor &= ~ACTORTAG_KEY;
1602 		}
1603 		else {
1604 			return;
1605 		}
1606 
1607 		SetActorTalking(actor, true);
1608 		SetActorTalkFilm(actor, hFilm);
1609 	}
1610 
1611 	if (bComplete) {
1612 		// Play to completion before returning
1613 		CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, 0, false, false, myEscape != 0, myEscape, bTop));
1614 	} else {
1615 		// Kick off the play and return.
1616 		CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, myEscape, bTop));
1617 	}
1618 
1619 	CORO_END_CODE;
1620 }
1621 
1622 
1623 /**
1624  * Play a midi file.
1625  */
PlayMidi(CORO_PARAM,SCNHANDLE hMidi,int loop,bool complete)1626 static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
1627 	CORO_BEGIN_CONTEXT;
1628 	CORO_END_CONTEXT(_ctx);
1629 
1630 	CORO_BEGIN_CODE(_ctx);
1631 	assert(loop == MIDI_DEF || loop == MIDI_LOOP);
1632 
1633 	PlayMidiSequence(hMidi, loop == MIDI_LOOP);
1634 
1635 	// This check&sleep was added in DW v2. It was most likely added to
1636 	// ensure that the MIDI song started playing before the next opcode
1637 	// is executed.
1638 	// In DW1, it messes up the script arguments when entering the secret
1639 	// door in the bookshelf in the library, leading to a crash, when the
1640 	// music volume is set to 0.
1641 	if (!MidiPlaying() && TinselV2)
1642 		CORO_SLEEP(1);
1643 
1644 	if (complete) {
1645 		while (MidiPlaying())
1646 			CORO_SLEEP(1);
1647 	}
1648 	CORO_END_CODE;
1649 }
1650 
1651 /**
1652  * Plays a movie
1653  */
1654 
PlayMovie(CORO_PARAM,SCNHANDLE hFileStem,int myEscape)1655 static void PlayMovie(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
1656 	CORO_BEGIN_CONTEXT;
1657 		int i;
1658 	CORO_END_CONTEXT(_ctx);
1659 
1660 	CORO_BEGIN_CODE(_ctx);
1661 
1662 	if (myEscape && myEscape != GetEscEvents())
1663 		return;
1664 
1665 	// Get rid of the cursor
1666 	for (_ctx->i = 0; _ctx->i < 3; _ctx->i++) {
1667 		DwHideCursor();
1668 		DropCursor();
1669 		CORO_SLEEP(1);
1670 	}
1671 
1672 	// They claim to be getting "Can't play two movies at once!" error
1673 	while (_vm->_bmv->MoviePlaying())
1674 		CORO_SLEEP(1);
1675 
1676 	// Play the movie
1677 	CORO_INVOKE_2(_vm->_bmv->PlayBMV, hFileStem, myEscape);
1678 
1679 	CORO_END_CODE;
1680 }
1681 
1682 /**
1683  * Play some music
1684  */
PlayMusic(int tune)1685 static void PlayMusic(int tune) {
1686 	_vm->_pcmMusic->startPlay(tune);
1687 }
1688 
1689 /**
1690  * Play a sample.
1691  * Tinsel 1 version
1692  */
PlaySample(CORO_PARAM,int sample,bool bComplete,bool escOn,int myEscape)1693 static void PlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
1694 	CORO_BEGIN_CONTEXT;
1695 		Audio::SoundHandle handle;
1696 	CORO_END_CONTEXT(_ctx);
1697 
1698 	CORO_BEGIN_CODE(_ctx);
1699 	// Don't play SFX if voice is already playing
1700 	if (_vm->_mixer->hasActiveChannelOfType(Audio::Mixer::kSpeechSoundType))
1701 		return;
1702 
1703 	// Don't do it if it's not wanted
1704 	if (escOn && myEscape != GetEscEvents()) {
1705 		_vm->_sound->stopAllSamples();		// Stop any currently playing sample
1706 		return;
1707 	}
1708 
1709 	if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
1710 		_vm->_sound->playSample(sample, Audio::Mixer::kSFXSoundType, &_ctx->handle);
1711 
1712 		if (bComplete) {
1713 			while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
1714 				// Abort if escapable and ESCAPE is pressed
1715 				if (escOn && myEscape != GetEscEvents()) {
1716 					_vm->_mixer->stopHandle(_ctx->handle);
1717 					break;
1718 				}
1719 
1720 				CORO_SLEEP(1);
1721 			}
1722 		}
1723 	} else {
1724 		// Prevent Glitter lock-up
1725 		CORO_SLEEP(1);
1726 	}
1727 	CORO_END_CODE;
1728 }
1729 
1730 /**
1731  * Play a sample
1732  * Tinsel 2 version
1733  */
PlaySample(CORO_PARAM,int sample,int x,int y,int flags,int myEscape)1734 static void PlaySample(CORO_PARAM, int sample, int x, int y, int flags, int myEscape) {
1735 	int	priority;
1736 	CORO_BEGIN_CONTEXT;
1737 		Audio::SoundHandle handle;
1738 		int myEscape;
1739 	CORO_END_CONTEXT(_ctx);
1740 
1741 	CORO_BEGIN_CODE(_ctx);
1742 
1743 	_ctx->myEscape = myEscape;
1744 
1745 	// Not escapable if PlaySample(..., s)
1746 	if (flags & PS_SUSTAIN) {
1747 		_ctx->myEscape = 0;
1748 		priority = PRIORITY_SPLAY2;
1749 	} else {
1750 		priority = PRIORITY_SPLAY1;
1751 	}
1752 
1753 	// Don't do anything if it's already been escaped
1754 	if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
1755 		return;
1756 
1757 	if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
1758 		if (x == 0)
1759 			x = -1;
1760 
1761 		_vm->_sound->playSample(sample, 0, false, x, y, priority, Audio::Mixer::kSFXSoundType,
1762 			&_ctx->handle);
1763 
1764 		if (flags & PS_COMPLETE) {
1765 			while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
1766 				// Abort if escapable and ESCAPE is pressed
1767 				if (_ctx->myEscape && _ctx->myEscape != GetEscEvents()) {
1768 					_vm->_mixer->stopHandle(_ctx->handle);
1769 					break;
1770 				}
1771 
1772 				CORO_SLEEP(1);
1773 			}
1774 		}
1775 	} else {
1776 		// Prevent Glitter lock-up
1777 		CORO_SLEEP(1);
1778 	}
1779 
1780 	CORO_END_CODE;
1781 }
1782 
1783 /**
1784  * Move the cursor to the tagged actor's tag point.
1785  */
PointActor(int actor)1786 void PointActor(int actor) {
1787 	int	x, y;
1788 
1789 	// Only do this if the function is enabled
1790 	if (!SysVar(SV_ENABLEPOINTTAG))
1791 		return;
1792 
1793 	assert(IsTaggedActor(actor));
1794 
1795 	GetActorTagPos(actor, &x, &y, true);
1796 
1797 	_vm->setMousePosition(Common::Point(x, y));
1798 }
1799 
1800 /**
1801  * Move the cursor to the tag's tag point.
1802  */
PointTag(int tagno,HPOLYGON hp)1803 static void PointTag(int tagno, HPOLYGON hp) {
1804 	int	x, y;
1805 	SCNHANDLE junk;
1806 
1807 	// Only do this if the function is enabled
1808 	if (!SysVar(SV_ENABLEPOINTTAG))
1809 		return;
1810 
1811 	// Tag could be zero, meaning calling tag
1812 	if (tagno == 0)
1813 		tagno = GetTagPolyId(hp);
1814 
1815 	GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
1816 
1817 	_vm->setMousePosition(Common::Point(x, y));
1818 }
1819 
1820 /**
1821  * PostActor("actor", event)
1822  */
PostActor(CORO_PARAM,int actor,TINSEL_EVENT event,HPOLYGON hp,int taggedActor,int myEscape)1823 static void PostActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp,
1824 			   int taggedActor, int myEscape) {
1825 	if (actor == -1) {
1826 		actor = taggedActor;
1827 		assert(hp == NOPOLY && taggedActor);
1828 		assert(IsTaggedActor(actor));
1829 	}
1830 
1831 	if (IsTaggedActor(actor)) {
1832 		assert(actor);
1833 		ActorEvent(coroParam, actor, event, false, myEscape);
1834 	} else {
1835 		PostTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape);
1836 	}
1837 }
1838 
1839 /**
1840  * PostGlobalProcess(process#, event)
1841  */
PostGlobalProcess(CORO_PARAM,int procId,TINSEL_EVENT event,int myEscape)1842 static void PostGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
1843 	GlobalProcessEvent(coroParam, procId, event, false, myEscape);
1844 }
1845 
1846 /**
1847  * PostObject(object, event)
1848  */
PostObject(CORO_PARAM,int object,TINSEL_EVENT event,int myEscape)1849 static void PostObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
1850 	ObjectEvent(coroParam, object, event, false, myEscape);
1851 }
1852 
1853 /**
1854  * PostProcess(process#, event)
1855  */
PostProcess(CORO_PARAM,int procId,TINSEL_EVENT event,int myEscape)1856 static void PostProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
1857 	SceneProcessEvent(coroParam, procId, event, false, myEscape);
1858 }
1859 
1860 /**
1861  * Posts an event to a specified tag
1862  */
PostTag(CORO_PARAM,int tagno,TINSEL_EVENT event,HPOLYGON hp,int myEscape)1863 static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
1864 	// Tag could be zero, meaning calling tag
1865 	if (tagno == 0) {
1866 		assert(hp != NOPOLY);
1867 		PolygonEvent(coroParam, hp, event, 0, false, myEscape);
1868 	} else {
1869 		assert(IsTagPolygon(tagno));
1870 		PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, false, myEscape);
1871 	}
1872 }
1873 
1874 /**
1875  * Trigger pre-loading of a scene's data.
1876  */
PrepareScene(SCNHANDLE scene)1877 static void PrepareScene(SCNHANDLE scene) {
1878 #ifdef BODGE
1879 	if (!ValidHandle(scene))
1880 		return;
1881 #endif
1882 }
1883 
1884 /**
1885  * Print the given text at the given place for the given time.
1886  */
Print(CORO_PARAM,int x,int y,SCNHANDLE text,int time,bool bSustain,bool escOn,int myEscape)1887 static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSustain, bool escOn, int myEscape) {
1888 	if (TinselV2)
1889 		escOn = myEscape != 0;
1890 
1891 	CORO_BEGIN_CONTEXT;
1892 		OBJECT *pText;			// text object pointer
1893 		int	myleftEvent;
1894 		bool bSample;			// Set if a sample is playing
1895 		Audio::SoundHandle handle;
1896 		int timeout;
1897 		int time;
1898 	CORO_END_CONTEXT(_ctx);
1899 
1900 	bool	bJapDoPrintText;	// Bodge to get-around Japanese bodge
1901 
1902 	CORO_BEGIN_CODE(_ctx);
1903 
1904 	_ctx->pText = NULL;
1905 	_ctx->bSample = false;
1906 
1907 	// Don't do it if it's not wanted
1908 	if (escOn && myEscape != GetEscEvents())
1909 		return;
1910 
1911 	if (!TinselV2) {
1912 		// Kick off the voice sample
1913 		if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
1914 			_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
1915 			_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
1916 		}
1917 	}
1918 
1919 	// Get the string
1920 	LoadStringRes(text, TextBufferAddr(), TBUFSZ);
1921 
1922 	// Calculate display time
1923 	bJapDoPrintText = false;
1924 	if (time == 0) {
1925 		// This is a 'talky' print
1926 		_ctx->time = TextTime(TextBufferAddr());
1927 
1928 		// Cut short-able if sustain was not set
1929 		_ctx->myleftEvent = bSustain ? 0 : GetLeftEvents();
1930 	} else {
1931 		_ctx->time = time * ONE_SECOND;
1932 		_ctx->myleftEvent = (TinselV2 && !bSustain) ? GetLeftEvents() : 0;
1933 		if (isJapanMode())
1934 			bJapDoPrintText = true;
1935 	}
1936 
1937 	// Print the text
1938 	if (TinselV2) {
1939 		int Loffset, Toffset;
1940 		PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
1941 		_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
1942 			TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(),
1943 			TXT_CENTER, 0);
1944 		assert(_ctx->pText);
1945 
1946 		// Adjust x, y, or z if necessary
1947 		KeepOnScreen(_ctx->pText, &x, &y);
1948 		if (IsTopWindow())
1949 			MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
1950 
1951 	} else if (bJapDoPrintText || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
1952 		int Loffset, Toffset;	// Screen position
1953 		PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
1954 		_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
1955 					0, x - Loffset, y - Toffset,
1956 					TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTER);
1957 		assert(_ctx->pText); // string produced NULL text
1958 		if (IsTopWindow())
1959 			MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
1960 
1961 		/*
1962 		 * New feature: Don't go off the side of the background
1963 		 */
1964 		int	shift;
1965 		shift = MultiRightmost(_ctx->pText) + 2;
1966 		if (shift >= BgWidth())			// Not off right
1967 			MultiMoveRelXY(_ctx->pText, BgWidth() - shift, 0);
1968 		shift = MultiLeftmost(_ctx->pText) - 1;
1969 		if (shift <= 0)					// Not off left
1970 			MultiMoveRelXY(_ctx->pText, -shift, 0);
1971 		shift = MultiLowest(_ctx->pText);
1972 		if (shift > BgHeight())			// Not off bottom
1973 			MultiMoveRelXY(_ctx->pText, 0, BgHeight() - shift);
1974 	}
1975 
1976 	// Give up if nothing printed and no sample
1977 	if (_ctx->pText == NULL && !_ctx->bSample)
1978 		return;
1979 
1980 	// Leave it up until time runs out or whatever
1981 	if (TinselV2) {
1982 		do {
1983 			CORO_SLEEP(1);
1984 
1985 			// Cancelled?
1986 			if ( (myEscape && myEscape != GetEscEvents())
1987 					|| (!bSustain && LeftEventChange(_ctx->myleftEvent)))
1988 				break;
1989 
1990 		} while (_ctx->time-- >= 0);
1991 
1992 	} else {
1993 		_ctx->timeout = SAMPLETIMEOUT;
1994 		do {
1995 			CORO_SLEEP(1);
1996 
1997 			// Abort if escapable and ESCAPE is pressed
1998 			// Abort if left click - hardwired feature for talky-print!
1999 			// Will be ignored if myleftevent happens to be 0!
2000 			// Abort if sample times out
2001 			if ((escOn && myEscape != GetEscEvents())
2002 			|| (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents())
2003 			|| (_ctx->bSample && --_ctx->timeout <= 0))
2004 				break;
2005 
2006 			if (_ctx->bSample) {
2007 				// Wait for sample to end whether or not
2008 				if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
2009 					if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED)				{
2010 						// No text or speed modification - just depends on sample
2011 						break;
2012 					} else {
2013 						// Must wait for time
2014 						_ctx->bSample = false;
2015 					}
2016 				}
2017 			} else {
2018 				// No sample - just depends on time
2019 				if (_ctx->time-- <= 0)
2020 					break;
2021 			}
2022 
2023 		} while (1);
2024 	}
2025 
2026 	// Delete the text
2027 	if (_ctx->pText != NULL)
2028 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
2029 	_vm->_mixer->stopHandle(_ctx->handle);
2030 
2031 	CORO_END_CODE;
2032 }
2033 
2034 
2035 static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
2036 static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
2037 
2038 /**
2039  * Print the given inventory object's name or whatever.
2040  */
PrintObj(CORO_PARAM,const SCNHANDLE hText,const INV_OBJECT * pinvo,const int event,int myEscape)2041 static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo, const int event, int myEscape) {
2042 	CORO_BEGIN_CONTEXT;
2043 		OBJECT *pText;		// text object pointer
2044 		int	textx, texty;
2045 		int	item;
2046 		bool bSample;
2047 		int sub;
2048 		Audio::SoundHandle handle;
2049 		int ticks;
2050 		int timeout;
2051 		bool bTookControl;
2052 		int myEscape;
2053 		int myLeftEvent;
2054 	CORO_END_CONTEXT(_ctx);
2055 
2056 	CORO_BEGIN_CODE(_ctx);
2057 
2058 	assert(pinvo != 0); // PrintObj() may only be called from an object code block
2059 	_ctx->myEscape = myEscape;
2060 
2061 	if (hText == (SCNHANDLE)-1) {	// 'OFF'
2062 		g_bNotPointedRunning = true;
2063 		return;
2064 	}
2065 	if (hText == (SCNHANDLE)-2) {	// 'ON'
2066 		g_bNotPointedRunning = false;
2067 		return;
2068 	}
2069 
2070 	// Don't do it if it's not wanted
2071 	if (TinselV2 && (myEscape) && (myEscape != GetEscEvents()))
2072 		return;
2073 
2074 	/*
2075 	* Find out which icon the cursor is over, and where to put the text.
2076 	*/
2077 	GetCursorXY(&_ctx->textx, &_ctx->texty, false);	// Cursor position..
2078 	_ctx->item = InvItem(&_ctx->textx, &_ctx->texty, true);	// ..to text position
2079 	if (_ctx->item == INV_NOICON)
2080 		return;
2081 
2082 	/*
2083 	* POINT/other event PrintObj() arbitration...
2084 	*/
2085 	if (event != POINTED) {
2086 		g_bNotPointedRunning = true;	// Get POINTED text to die
2087 		CORO_SLEEP(1);		// Give it chance to
2088 	} else if (!TinselV2)
2089 		g_bNotPointedRunning = false;	// There may have been an OFF without an ON
2090 
2091 	// Make multi-ones escape
2092 	if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->myEscape)
2093 		_ctx->myEscape = GetEscEvents();
2094 
2095 	// Loop once for Tinsel 1 strings, and for Tinsel 2 however many lines are needed
2096 	for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
2097 		if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
2098 			break;
2099 
2100 		if (!_vm->_sound->sampleExists(hText))
2101 			_ctx->bSample = false;
2102 		else {
2103 			// Kick off the voice sample
2104 			_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK,
2105 				Audio::Mixer::kSpeechSoundType, &_ctx->handle);
2106 			_ctx->bSample = true;
2107 		}
2108 
2109 		// Display the text and set it's Z position
2110 		if (event == POINTED || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
2111 			int	xshift;
2112 
2113 			// Get the text string
2114 			if (TinselV2)
2115 				LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
2116 			else
2117 				LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
2118 
2119 			_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
2120 						0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTER);
2121 			assert(_ctx->pText); // PrintObj() string produced NULL text
2122 
2123 			MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
2124 
2125 			if (TinselV2)
2126 				KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
2127 			else {
2128 				// Don't go off the side of the screen
2129 				xshift = MultiLeftmost(_ctx->pText);
2130 				if (xshift < 0) {
2131 					MultiMoveRelXY(_ctx->pText, - xshift, 0);
2132 					_ctx->textx -= xshift;
2133 				}
2134 				xshift = MultiRightmost(_ctx->pText);
2135 				if (xshift > SCREEN_WIDTH) {
2136 					MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
2137 					_ctx->textx += SCREEN_WIDTH - xshift;
2138 				}
2139 			}
2140 		} else
2141 			_ctx->pText = NULL;
2142 
2143 		if (TinselV2) {
2144 			if (event == POINTED) {
2145 				/*
2146 				* PrintObj() called from POINT event
2147 				*/
2148 				// Have to give way to non-POINTED-generated text
2149 				// and go away if the item gets picked up
2150 				int x, y;
2151 				do {
2152 					// Give up if this item gets picked up
2153 					if (WhichItemHeld() == pinvo->id)
2154 						break;
2155 
2156 					// Give way to non-POINTED-generated text
2157 					if (g_bNotPointedRunning) {
2158 						// Delete the text, and wait for the all-clear
2159 						MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
2160 						_ctx->pText = NULL;
2161 
2162 						while (g_bNotPointedRunning)
2163 							CORO_SLEEP(1);
2164 
2165 						GetCursorXY(&x, &y, false);
2166 						if (InvItem(&x, &y, false) != _ctx->item)
2167 							break;
2168 
2169 						// Re-display in the same place
2170 						LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
2171 						_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
2172 							TextBufferAddr(), 0, _ctx->textx, _ctx->texty, GetTagFontHandle(),
2173 							TXT_CENTER, 0);
2174 						assert(_ctx->pText);
2175 
2176 						KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
2177 						MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
2178 					}
2179 
2180 					CORO_SLEEP(1);
2181 
2182 					// Carry on until the cursor leaves this icon
2183 					GetCursorXY(&x, &y, false);
2184 
2185 				} while (InvItemId(x, y) == pinvo->id);
2186 			} else {
2187 				/*
2188 				 * PrintObj() called from other event
2189 				 */
2190 				_ctx->myLeftEvent = GetLeftEvents();
2191 				_ctx->bTookControl = GetControl();
2192 
2193 				// Display for a time, but abort if conversation gets hidden
2194 				if (_ctx->pText)
2195 					_ctx->ticks = TextTime(TextBufferAddr());
2196 				_ctx->timeout = SAMPLETIMEOUT;
2197 
2198 				for (;;) {
2199 					CORO_SLEEP(1);
2200 
2201 					// Abort if left click - hardwired feature for talky-print!
2202 					// Abort if sample times out
2203 					// Abort if conversation hidden
2204 					if (LeftEventChange(_ctx->myLeftEvent)
2205 							|| --_ctx->timeout <= 0
2206 							|| ConvIsHidden())
2207 						break;
2208 
2209 					if (_ctx->bSample) {
2210 						// Wait for sample to end whether or not
2211 						if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
2212 							if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
2213 								// No text or speed modification - just depends on sample
2214 								break;
2215 							} else {
2216 								// Must wait for time
2217 								_ctx->bSample = false;
2218 							}
2219 						}
2220 
2221 						// Decrement the subtitles timeout counter
2222 						if (_ctx->ticks > 0) --_ctx->ticks;
2223 
2224 					} else {
2225 						// No sample - just depends on time
2226 						if (_ctx->ticks-- <= 0)
2227 							break;
2228 					}
2229 				}
2230 
2231 				if (_ctx->bTookControl)
2232 					ControlOn();		// Free control if we took it
2233 			}
2234 
2235 		} else {
2236 			if (event == POINTED) {
2237 				// FIXME: Is there ever an associated sound if in POINTED mode???
2238 				assert(!_vm->_sound->sampleExists(hText));
2239 				CORO_INVOKE_ARGS(PrintObjPointed, (CORO_SUBCTX, hText, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item));
2240 			} else {
2241 				CORO_INVOKE_2(PrintObjNonPointed, hText, _ctx->pText);
2242 			}
2243 		}
2244 
2245 		// Delete the text, if haven't already
2246 		if (_ctx->pText)
2247 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
2248 
2249 		// If it hasn't already finished, stop sample
2250 		if (_ctx->bSample)
2251 			_vm->_mixer->stopHandle(_ctx->handle);
2252 	}
2253 
2254 	// Let POINTED text back in if this is the last
2255 	if (event != POINTED)
2256 		g_bNotPointedRunning = false;
2257 
2258 	CORO_END_CODE;
2259 }
2260 
PrintObjPointed(CORO_PARAM,const SCNHANDLE text,const INV_OBJECT * pinvo,OBJECT * & pText,const int textx,const int texty,const int item)2261 static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
2262 	CORO_BEGIN_CONTEXT;
2263 	CORO_END_CONTEXT(_ctx);
2264 
2265 	CORO_BEGIN_CODE(_ctx);
2266 		// Have to give way to non-POINTED-generated text
2267 		// and go away if the item gets picked up
2268 		int	x, y;
2269 		do {
2270 			// Give up if this item gets picked up
2271 			if (WhichItemHeld() == pinvo->id)
2272 				break;
2273 
2274 			// Give way to non-POINTED-generated text
2275 			if (g_bNotPointedRunning) {
2276 				// Delete the text, and wait for the all-clear
2277 				MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), pText);
2278 				pText = NULL;
2279 				while (g_bNotPointedRunning)
2280 					CORO_SLEEP(1);
2281 
2282 				GetCursorXY(&x, &y, false);
2283 				if (InvItem(&x, &y, false) != item)
2284 					break;
2285 
2286 				// Re-display in the same place
2287 				LoadStringRes(text, TextBufferAddr(), TBUFSZ);
2288 				pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
2289 							0, textx, texty, GetTagFontHandle(), TXT_CENTER);
2290 				assert(pText); // PrintObj() string produced NULL text
2291 				MultiSetZPosition(pText, Z_INV_ITEXT);
2292 			}
2293 
2294 			CORO_SLEEP(1);
2295 
2296 			// Carry on until the cursor leaves this icon
2297 			GetCursorXY(&x, &y, false);
2298 		} while (InvItemId(x, y) == pinvo->id);
2299 
2300 	CORO_END_CODE;
2301 }
2302 
PrintObjNonPointed(CORO_PARAM,const SCNHANDLE text,const OBJECT * pText)2303 static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) {
2304 	CORO_BEGIN_CONTEXT;
2305 		bool bSample;		// Set if a sample is playing
2306 		Audio::SoundHandle handle;
2307 
2308 		int myleftEvent;
2309 		bool took_control;
2310 		int	ticks;
2311 		int	timeout;
2312 	CORO_END_CONTEXT(_ctx);
2313 
2314 	CORO_BEGIN_CODE(_ctx);
2315 		// Kick off the voice sample
2316 		if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
2317 			_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
2318 			_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
2319 		} else
2320 			_ctx->bSample = false;
2321 
2322 		_ctx->myleftEvent = GetLeftEvents();
2323 		_ctx->took_control = GetControl(CONTROL_OFF);
2324 
2325 		// Display for a time, but abort if conversation gets hidden
2326 		if (isJapanMode())
2327 			_ctx->ticks = JAP_TEXT_TIME;
2328 		else if (pText)
2329 			_ctx->ticks = TextTime(TextBufferAddr());
2330 		else
2331 			_ctx->ticks = 0;
2332 
2333 		_ctx->timeout = SAMPLETIMEOUT;
2334 		do {
2335 			CORO_SLEEP(1);
2336 			--_ctx->timeout;
2337 
2338 			// Abort if left click - hardwired feature for talky-print!
2339 			// Abort if sample times out
2340 			// Abort if conversation hidden
2341 			if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || ConvIsHidden())
2342 				break;
2343 
2344 			if (_ctx->bSample) {
2345 				// Wait for sample to end whether or not
2346 				if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
2347 					if (pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
2348 						// No text or speed modification - just depends on sample
2349 						break;
2350 					} else {
2351 						// Must wait for time
2352 						_ctx->bSample = false;
2353 					}
2354 				}
2355 
2356 				// Decrement the subtitles timeout counter
2357 				if (_ctx->ticks > 0) --_ctx->ticks;
2358 
2359 			} else {
2360 				// No sample - just depends on time
2361 				if (_ctx->ticks-- <= 0)
2362 					break;
2363 			}
2364 		} while (1);
2365 
2366 		g_bNotPointedRunning = false;	// Let POINTED text back in
2367 
2368 		if (_ctx->took_control)
2369 			Control(CONTROL_ON);	// Free control if we took it
2370 
2371 		_vm->_mixer->stopHandle(_ctx->handle);
2372 
2373 	CORO_END_CODE;
2374 }
2375 
2376 /**
2377  * Register the fact that this poly would like its tag displayed.
2378  */
PrintTag(HPOLYGON hp,SCNHANDLE text,int actor=0,bool bCursor=false)2379 static void PrintTag(HPOLYGON hp, SCNHANDLE text, int actor = 0, bool bCursor = false) {
2380 	// printtag() may only be called from a polygon code block in Tinsel 1, or
2381 	// additionally from a moving actor code block in Tinsel 2
2382 	assert((hp != NOPOLY) || (TinselV2 && (actor != 0)));
2383 
2384 	if (hp != NOPOLY) {
2385 		// Poly handling
2386 		if (TinselV2)
2387 			SetPolyTagWanted(hp, true, bCursor, text);
2388 		else if (PolyTagState(hp) == TAG_OFF) {
2389 			SetPolyTagState(hp, TAG_ON);
2390 			SetPolyTagHandle(hp, text);
2391 		}
2392 	} else {
2393 		// Moving actor handling
2394 		SetActorTagWanted(actor, true, bCursor, text);
2395 	}
2396 }
2397 
2398 /**
2399  * Quits the game
2400  */
QuitGame()2401 static void QuitGame() {
2402 	StopMidi();
2403 	StopSample();
2404 	_vm->quitGame();
2405 }
2406 
2407 /**
2408  * Return a random number between optional limits.
2409  */
RandomFn(int n1,int n2,int norpt)2410 static int RandomFn(int n1, int n2, int norpt) {
2411 	int i = 0;
2412 	uint32 value;
2413 
2414 	// In DW1 demo, upper/lower limit can be reversed
2415 	if (n2 < n1) SWAP(n1, n2);
2416 
2417 	do {
2418 		value = n1 + _vm->getRandomNumber(n2 - n1);
2419 	} while ((g_lastValue == value) && (norpt == RAND_NORPT) && (++i <= 10));
2420 
2421 	g_lastValue = value;
2422 	return value;
2423 }
2424 
2425 /**
2426  * ResetIdleTime
2427  */
ResetIdleTime()2428 void ResetIdleTime() {
2429 	resetUserEventTime();
2430 }
2431 
2432 /**
2433  * FnRestartGame
2434  */
FnRestartGame()2435 void FnRestartGame() {
2436 	// TODO: Tinsel 2 comments out the 2 calls, but I'm not sure that this should be done
2437 	StopMidi();
2438 	StopSample();
2439 
2440 	g_bRestart = true;
2441 	g_sceneCtr = 0;
2442 }
2443 
2444 /**
2445  * Restore saved scene.
2446  */
RestoreScene(CORO_PARAM,TRANSITS transition)2447 static void RestoreScene(CORO_PARAM, TRANSITS transition) {
2448 	// COROUTINE
2449 	CORO_BEGIN_CONTEXT;
2450 	CORO_END_CONTEXT(_ctx);
2451 
2452 	CORO_BEGIN_CODE(_ctx);
2453 
2454 	if (TinselV2) {
2455 		if (_vm->_bmv->MoviePlaying()) {
2456 			_vm->_bmv->AbortMovie();
2457 			CORO_SLEEP(2);
2458 		}
2459 
2460 		CuttingScene(false);
2461 
2462 	} else {
2463 		UnSuspendHook();
2464 	}
2465 
2466 	TinselRestoreScene(transition == TRANS_FADE);
2467 
2468 	CORO_END_CODE;
2469 }
2470 
2471 /**
2472  * Resumes the last game
2473  */
ResumeLastGame()2474 void ResumeLastGame() {
2475 	RestoreGame(NewestSavedGame());
2476 }
2477 
2478 /**
2479  * Returns the current run mode
2480  */
RunMode()2481 static int RunMode() {
2482 	return 0;	//clRunMode;
2483 }
2484 
2485 /**
2486  * SamplePlaying
2487  */
SamplePlaying(bool escOn,int myEscape)2488 static bool SamplePlaying(bool escOn, int myEscape) {
2489 	// escape effects introduced 14/12/95 to fix
2490 	//	 while (sampleplaying()) pause;
2491 
2492 	if (escOn && myEscape != GetEscEvents())
2493 		return false;
2494 
2495 	return _vm->_sound->sampleIsPlaying();
2496 }
2497 
2498 /**
2499  * Save current scene.
2500  */
SaveScene(CORO_PARAM)2501 void SaveScene(CORO_PARAM) {
2502 	CORO_BEGIN_CONTEXT;
2503 	CORO_END_CONTEXT(_ctx);
2504 
2505 	CORO_BEGIN_CODE(_ctx);
2506 
2507 	if (TinselV2) {
2508 		CuttingScene(true);
2509 		SendSceneTinselProcess(LEAVE_T2);
2510 		CORO_GIVE_WAY;
2511 
2512 		CORO_INVOKE_0(TinselSaveScene);
2513 	} else {
2514 		CORO_INVOKE_0(TinselSaveScene);
2515 		SuspendHook();
2516 	}
2517 
2518 	CORO_END_CODE;
2519 }
2520 
2521 /**
2522  * ScalingReels
2523  */
ScalingReels(int actor,int scale,int direction,SCNHANDLE left,SCNHANDLE right,SCNHANDLE forward,SCNHANDLE away)2524 static void ScalingReels(int actor, int scale, int direction,
2525 		SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
2526 
2527 	SetScalingReels(actor, scale, direction, left, right, forward, away);
2528 }
2529 
2530 /**
2531  * Return the icon that caused the CONVERSE event.
2532  */
ScanIcon()2533 static int ScanIcon() {
2534 	return GetIcon();
2535 }
2536 
2537 /**
2538  * Scroll the screen to target co-ordinates.
2539  */
Scroll(CORO_PARAM,EXTREME extreme,int xp,int yp,int xIter,int yIter,bool bComp,bool escOn,int myEscape)2540 static void Scroll(CORO_PARAM, EXTREME extreme, int xp, int yp, int xIter, int yIter, bool bComp, bool escOn, int myEscape) {
2541 	CORO_BEGIN_CONTEXT;
2542 		int	thisScroll;
2543 		int x, y;
2544 	CORO_END_CONTEXT(_ctx);
2545 
2546 	CORO_BEGIN_CODE(_ctx);
2547 
2548 	++g_scrollNumber;
2549 	_ctx->x = xp;
2550 	_ctx->y = yp;
2551 
2552 	if ((TinselV2 && g_bInstantScroll) || (escOn && myEscape != GetEscEvents())) {
2553 		// Instant completion!
2554 		Offset(extreme, _ctx->x, _ctx->y);
2555 	} else {
2556 		_ctx->thisScroll = g_scrollNumber;
2557 		if (TinselV2)
2558 			DecodeExtreme(extreme, &_ctx->x, &_ctx->y);
2559 
2560 		ScrollTo(_ctx->x, _ctx->y, xIter, yIter);
2561 
2562 		if (bComp) {
2563 			int	Loffset, Toffset;
2564 			do {
2565 				CORO_SLEEP(1);
2566 
2567 				// If escapable and ESCAPE is pressed...
2568 				if (escOn && myEscape != GetEscEvents()) {
2569 					// Instant completion!
2570 					Offset(extreme, _ctx->x, _ctx->y);
2571 					break;
2572 				}
2573 
2574 				// give up if have been superseded
2575 				if (_ctx->thisScroll != g_scrollNumber)
2576 					CORO_KILL_SELF();
2577 
2578 				PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
2579 			} while (Loffset != _ctx->x || Toffset != _ctx->y);
2580 		} else if (TinselV2 && myEscape) {
2581 			SCROLL_MONITOR sm;
2582 
2583 			// Scroll is escapable even though we're not waiting for it
2584 			sm.x = _ctx->x;
2585 			sm.y = _ctx->y;
2586 			sm.thisScroll = g_scrollNumber;
2587 			sm.myEscape = myEscape;
2588 			CoroScheduler.createProcess(PID_TCODE, ScrollMonitorProcess, &sm, sizeof(sm));
2589 		}
2590 	}
2591 	CORO_END_CODE;
2592 }
2593 
2594 /**
2595  * ScrollParameters
2596  */
ScrollParameters(int xTrigger,int xDistance,int xSpeed,int yTriggerTop,int yTriggerBottom,int yDistance,int ySpeed)2597 static void ScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
2598 		int yTriggerBottom, int yDistance, int ySpeed) {
2599 	SetScrollParameters(xTrigger, xDistance, xSpeed,
2600 			yTriggerTop, yTriggerBottom, yDistance, ySpeed);
2601 }
2602 
2603 /**
2604  * SendActor("actor", event)
2605  */
SendActor(CORO_PARAM,int actor,TINSEL_EVENT event,HPOLYGON hp,int myEscape)2606 int SendActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
2607 	bool result;
2608 
2609 	if (IsTaggedActor(actor)) {
2610 		assert(actor);
2611 		ActorEvent(coroParam, actor, event, true, myEscape, &result);
2612 	} else {
2613 		SendTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape, &result);
2614 	}
2615 
2616 	return result;
2617 }
2618 
2619 /**
2620  * SendGlobalProcess(process#, event)
2621  */
SendGlobalProcess(CORO_PARAM,int procId,TINSEL_EVENT event,int myEscape)2622 static int SendGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
2623 	return GlobalProcessEvent(coroParam, procId, event, true, myEscape);
2624 }
2625 
2626 /**
2627  * SendObject(object, event)
2628  */
SendObject(CORO_PARAM,int object,TINSEL_EVENT event,int myEscape)2629 static int SendObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
2630 	bool result;
2631 	ObjectEvent(coroParam, object, event, true, myEscape, &result);
2632 	return result;
2633 }
2634 
2635 /**
2636  * SendProcess(process#, event)
2637  */
SendProcess(CORO_PARAM,int procId,TINSEL_EVENT event,int myEscape)2638 static int SendProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
2639 	bool result;
2640 	SceneProcessEvent(coroParam, procId, event, true, myEscape, &result);
2641 	return result;
2642 }
2643 
2644 /**
2645  * SendTag(tag#, event)
2646  */
SendTag(CORO_PARAM,int tagno,TINSEL_EVENT event,HPOLYGON hp,int myEscape,bool * result)2647 static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result) {
2648 	// Tag could be zero, meaning calling tag
2649 	if (tagno == 0) {
2650 		assert(hp != NOPOLY);
2651 
2652 		PolygonEvent(coroParam, hp, event, 0, true, myEscape, result);
2653 	} else {
2654 		assert(IsTagPolygon(tagno));
2655 
2656 		PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, true, myEscape, result);
2657 	}
2658 }
2659 
2660 /**
2661  * Un-kill an actor.
2662  */
SetActor(int actor)2663 static void SetActor(int actor) {
2664 	EnableActor(actor);
2665 }
2666 
2667 /**
2668  * Turn a blocking polygon on.
2669  */
2670 
SetBlock(int blockno)2671 static void SetBlock(int blockno) {
2672 	EnableBlock(blockno);
2673 }
2674 
2675 /**
2676  * Turn an exit on.
2677  */
2678 
SetExit(int exitno)2679 static void SetExit(int exitno) {
2680 	EnableExit(exitno);
2681 }
2682 
2683 /**
2684  * Guess what.
2685  */
SetInvLimit(int invno,int n)2686 static void SetInvLimit(int invno, int n) {
2687 	InvSetLimit(invno, n);
2688 }
2689 
2690 /**
2691  * Guess what.
2692  */
SetInvSize(int invno,int MinWidth,int MinHeight,int StartWidth,int StartHeight,int MaxWidth,int MaxHeight)2693 static void SetInvSize(int invno, int MinWidth, int MinHeight,
2694 		int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
2695 	InvSetSize(invno, MinWidth, MinHeight, StartWidth, StartHeight, MaxWidth, MaxHeight);
2696 }
2697 
2698 /**
2699  * Guess what.
2700  */
SetLanguage(LANGUAGE lang)2701 static void SetLanguage(LANGUAGE lang) {
2702 	assert(lang == TXT_ENGLISH || lang == TXT_FRENCH
2703 	     || lang == TXT_GERMAN  || lang == TXT_ITALIAN
2704 	     || lang == TXT_SPANISH); // ensure language is valid
2705 
2706 	ChangeLanguage(lang);
2707 }
2708 
2709 /**
2710  * Set palette
2711  */
SetPalette(SCNHANDLE hPal,bool escOn,int myEscape)2712 static void SetPalette(SCNHANDLE hPal, bool escOn, int myEscape) {
2713 	// Don't do it if it's not wanted
2714 	if (escOn && myEscape != GetEscEvents())
2715 		return;
2716 
2717 	ChangePalette(hPal);
2718 }
2719 
2720 /**
2721  * SetSystemString
2722  */
2723 
SetSystemString(int stringId,SCNHANDLE hString)2724 static void SetSystemString(int stringId, SCNHANDLE hString) {
2725 	SetSysString(stringId, hString);
2726 }
2727 
2728 /**
2729  * Set a system variable
2730  */
SetSystemVar(int varId,int newValue)2731 static void SetSystemVar(int varId, int newValue) {
2732 	SetSysVar(varId, newValue);
2733 }
2734 
2735 /**
2736  * Turn a tag on.
2737  */
SetTag(CORO_PARAM,int tagno)2738 static void SetTag(CORO_PARAM, int tagno) {
2739 	EnableTag(coroParam, tagno);
2740 }
2741 
2742 /**
2743  * Initialize a timer.
2744  */
SetTimer(int timerno,int start,bool up,bool frame)2745 static void SetTimer(int timerno, int start, bool up, bool frame) {
2746 	StartTimer(timerno, start, up != 0, frame != 0);
2747 }
2748 
2749 /**
2750  * Shell("cmdline")
2751  */
Shell(SCNHANDLE commandLine)2752 static void Shell(SCNHANDLE commandLine) {
2753 	LoadStringRes(commandLine, TextBufferAddr(), TBUFSZ);
2754 	error("Tried to execute shell command \"%s\"", TextBufferAddr());
2755 }
2756 
2757 /**
2758  * Don't hide an actors graphics.
2759  */
ShowActorFn(CORO_PARAM,int actor)2760 static void ShowActorFn(CORO_PARAM, int actor) {
2761 	ShowActor(coroParam, actor);
2762 }
2763 
2764 /**
2765  * Turn a blocking polygon on.
2766  */
ShowBlock(int blockno)2767 void ShowBlock(int blockno) {
2768 	EnableBlock(blockno);
2769 }
2770 
2771 /**
2772  * Turn an effect polygon on.
2773  */
ShowEffect(int effect)2774 void ShowEffect(int effect) {
2775 	EnableEffect(effect);
2776 }
2777 
2778 #ifdef DEBUG
2779 /**
2780  * Enable display of diagnostic co-ordinates.
2781  */
showpos()2782 static void showpos() {
2783 	setshowpos();
2784 }
2785 
2786 /**
2787  * Enable display of diagnostic co-ordinates.
2788  */
showstring()2789 static void showstring() {
2790 	setshowstring();
2791 }
2792 #endif
2793 
2794 /**
2795  * Shows the main menu
2796  */
ShowMenu()2797 static void ShowMenu() {
2798 	OpenMenu(MAIN_MENU);
2799 }
2800 
2801 /**
2802  * Turn a path on.
2803  */
ShowPath(int path)2804 static void ShowPath(int path) {
2805 	EnablePath(path);
2806 }
2807 
2808 /**
2809  * Turn a refer on.
2810  */
ShowRefer(int refer)2811 void ShowRefer(int refer) {
2812 	EnableRefer(refer);
2813 }
2814 
2815 /**
2816  * Turn a tag on.
2817  */
ShowTag(CORO_PARAM,int tag,HPOLYGON hp)2818 static void ShowTag(CORO_PARAM, int tag, HPOLYGON hp) {
2819 	// Tag could be zero, meaning calling tag
2820 	EnableTag(coroParam, tag ? tag : GetTagPolyId(hp));
2821 }
2822 
2823 /**
2824  * Special play - slow down associated actor's movement while the play
2825  * is running. After the play, position the actor where the play left
2826  * it and continue walking, if the actor still is.
2827  */
SPlay(CORO_PARAM,int sf,SCNHANDLE film,int x,int y,bool complete,int actorid,bool escOn,int myEscape)2828 static void SPlay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myEscape) {
2829 	// Don't do it if it's not wanted
2830 	if (escOn && myEscape != GetEscEvents())
2831 		return;
2832 
2833 	Play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myEscape, false);
2834 }
2835 
2836 /**
2837  * (Re)Position an actor.
2838  * If moving actor is not around yet in this scene, start it up.
2839  */
Stand(CORO_PARAM,int actor,int x,int y,SCNHANDLE hFilm)2840 void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm) {
2841 	CORO_BEGIN_CONTEXT;
2842 		PMOVER pMover;		// Moving actor structure
2843 	CORO_END_CONTEXT(_ctx);
2844 
2845 	CORO_BEGIN_CODE(_ctx);
2846 
2847 	_ctx->pMover = GetMover(actor);
2848 	assert(!TinselV2 || (_ctx->pMover != NULL));
2849 
2850 	if (_ctx->pMover) {
2851 		if (TinselV2) {
2852 			// New special. If no paths, just ignore this
2853 			if (PathCount() == 0)
2854 				return;
2855 
2856 			// Another new special.
2857 			// If lead actor, and TalkVia, ignore
2858 			if ((actor == GetLeadId() || actor == LEAD_ACTOR) && SysVar(ISV_DIVERT_ACTOR))
2859 				return;
2860 		}
2861 
2862 		if (!MoverIs(_ctx->pMover)) {
2863 			// create a moving actor process
2864 			MoverProcessCreate(x, y, (actor == LEAD_ACTOR) ? GetLeadId() : actor, _ctx->pMover);
2865 
2866 			if (hFilm == TF_NONE) {
2867 				// Make sure there is an assigned actorObj
2868 				while (!_ctx->pMover->actorObj)
2869 					CORO_SLEEP(1);
2870 
2871 				SetMoverStanding(_ctx->pMover);
2872 			} else {
2873 				// Check hFilm against certain constants. Note that a switch statement isn't
2874 				// used here because it would interfere with our co-routine implementation
2875 				if (hFilm == TF_UP) {
2876 					if (TinselV2) CORO_GIVE_WAY;
2877 					SetMoverDirection(_ctx->pMover, AWAY);
2878 					SetMoverStanding(_ctx->pMover);
2879 				} else if (hFilm == TF_DOWN) {
2880 					if (TinselV2) CORO_GIVE_WAY;
2881 					SetMoverDirection(_ctx->pMover, FORWARD);
2882 					SetMoverStanding(_ctx->pMover);
2883 				} else if (hFilm == TF_LEFT) {
2884 					if (TinselV2) CORO_GIVE_WAY;
2885 					SetMoverDirection(_ctx->pMover, LEFTREEL);
2886 					SetMoverStanding(_ctx->pMover);
2887 				} else if (hFilm == TF_RIGHT) {
2888 					if (TinselV2) CORO_GIVE_WAY;
2889 					SetMoverDirection(_ctx->pMover, RIGHTREEL);
2890 					SetMoverStanding(_ctx->pMover);
2891 				} else if (hFilm != TF_NONE) {
2892 					if (TinselV2) CORO_GIVE_WAY;
2893 					AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
2894 				}
2895 			}
2896 		} else {
2897 			switch (hFilm) {
2898 			case TF_NONE:
2899 				if (x != -1 && y != -1)
2900 					PositionMover(_ctx->pMover, x, y);
2901 				break;
2902 
2903 			case TF_UP:
2904 				SetMoverDirection(_ctx->pMover, AWAY);
2905 				if (x != -1 && y != -1)
2906 					PositionMover(_ctx->pMover, x, y);
2907 				SetMoverStanding(_ctx->pMover);
2908 				break;
2909 			case TF_DOWN:
2910 				SetMoverDirection(_ctx->pMover, FORWARD);
2911 				if (x != -1 && y != -1)
2912 					PositionMover(_ctx->pMover, x, y);
2913 				SetMoverStanding(_ctx->pMover);
2914 				break;
2915 			case TF_LEFT:
2916 				SetMoverDirection(_ctx->pMover, LEFTREEL);
2917 				if (x != -1 && y != -1)
2918 					PositionMover(_ctx->pMover, x, y);
2919 				SetMoverStanding(_ctx->pMover);
2920 				break;
2921 			case TF_RIGHT:
2922 				SetMoverDirection(_ctx->pMover, RIGHTREEL);
2923 				if (x != -1 && y != -1)
2924 					PositionMover(_ctx->pMover, x, y);
2925 				SetMoverStanding(_ctx->pMover);
2926 				break;
2927 
2928 			default:
2929 				if (x != -1 && y != -1)
2930 					PositionMover(_ctx->pMover, x, y);
2931 				AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
2932 				break;
2933 			}
2934 		}
2935 	} else if (actor == NULL_ACTOR) {
2936 		//
2937 	} else {
2938 		assert(hFilm != 0); // Trying to play NULL film
2939 
2940 		// Kick off the play and return.
2941 		CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actor, false, 0, false, 0, false));
2942 	}
2943 
2944 	CORO_END_CODE;
2945 }
2946 
2947 /**
2948  * Position the actor at the polygon's tag node.
2949  */
StandTag(int actor,HPOLYGON hp)2950 static void StandTag(int actor, HPOLYGON hp) {
2951 	SCNHANDLE hFilm;
2952 	int	pnodex, pnodey;
2953 
2954 	assert(hp != NOPOLY); // StandTag() may only be called from a polygon code block
2955 
2956 	// Where to stand
2957 	GetPolyNode(hp, &pnodex, &pnodey);
2958 
2959 	// Lead actor uses tag node film
2960 	hFilm = GetPolyFilm(hp);
2961 
2962 	// other actors can use direction
2963 	if (TinselV2) {
2964 		if (actor != LEAD_ACTOR && actor != GetLeadId()
2965 				&& hFilm != TF_UP && hFilm != TF_DOWN
2966 				&& hFilm != TF_LEFT && hFilm != TF_RIGHT)
2967 			hFilm = 0;
2968 
2969 		Stand(Common::nullContext, actor, pnodex, pnodey, hFilm);
2970 
2971 	} else if (hFilm && (actor == LEAD_ACTOR || actor == GetLeadId()))
2972 		Stand(Common::nullContext, actor, pnodex, pnodey, hFilm);
2973 	else
2974 		Stand(Common::nullContext, actor, pnodex, pnodey, 0);
2975 }
2976 
2977 
2978 /**
2979  * StartGlobalProcess
2980  */
StartGlobalProcess(CORO_PARAM,uint32 procID)2981 static void StartGlobalProcess(CORO_PARAM, uint32 procID) {
2982 	GlobalProcessEvent(coroParam, procID, STARTUP, false, 0);
2983 }
2984 
2985 /**
2986  * StartProcess
2987  */
StartProcess(CORO_PARAM,uint32 procID)2988 static void StartProcess(CORO_PARAM, uint32 procID) {
2989 	SceneProcessEvent(coroParam, procID, STARTUP, false, 0);
2990 }
2991 
2992 /**
2993  * Initialize a timer.
2994  */
StartTimerFn(int timerno,int start,bool up,int fs)2995 static void StartTimerFn(int timerno, int start, bool up, int fs) {
2996 	StartTimer(timerno, start, up, fs);
2997 }
2998 
StopMidiFn()2999 void StopMidiFn() {
3000 	StopMidi();		// Stop any currently playing midi
3001 }
3002 
3003 /**
3004  * Kill a specific sample, or all samples.
3005  */
StopSample(int sample)3006 void StopSample(int sample) {
3007 	if (sample == -1)
3008 		_vm->_sound->stopAllSamples();		// Stop any currently playing sample
3009 	else
3010 		_vm->_sound->stopSpecSample(sample, 0);
3011 }
3012 
3013 /**
3014  * Kill a moving actor's walk.
3015  */
StopWalk(int actor)3016 static void StopWalk(int actor) {
3017 	PMOVER pMover;
3018 
3019 	pMover = GetMover(actor);
3020 	assert(pMover);
3021 
3022 	if (TinselV2) {
3023 		if (MoverHidden(pMover))
3024 			return;
3025 
3026 		StopMover(pMover);		// Cause the actor to stop
3027 	} else {
3028 		GetToken(pMover->actorToken);	// Kill the walk process
3029 		pMover->bStop = true;			// Cause the actor to stop
3030 		FreeToken(pMover->actorToken);
3031 	}
3032 }
3033 
3034 /**
3035  * Subtitles on/off
3036  */
Subtitles(int onoff)3037 static void Subtitles(int onoff) {
3038 	assert (onoff == ST_ON || onoff == ST_OFF);
3039 
3040 	if (isJapanMode())
3041 		return;	// Subtitles are always off in JAPAN version (?)
3042 
3043 	_vm->_config->_useSubtitles = (onoff == ST_ON);
3044 }
3045 
3046 /**
3047  * Special walk.
3048  * Walk into or out of a legal path.
3049  */
Swalk(CORO_PARAM,int actor,int x1,int y1,int x2,int y2,SCNHANDLE film,int32 zOverride,bool escOn,int myEscape)3050 static void Swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, int32 zOverride, bool escOn, int myEscape) {
3051 	CORO_BEGIN_CONTEXT;
3052 		bool	bTookControl;			// Set if this function takes control
3053 	CORO_END_CONTEXT(_ctx);
3054 
3055 	HPOLYGON hPath;
3056 
3057 	CORO_BEGIN_CODE(_ctx);
3058 
3059 	// Don't do it if it's not wanted
3060 	if (escOn && myEscape != GetEscEvents()) {
3061 		if (TinselV2) {
3062 			if (x2 == -1 && y2 == -1)
3063 				CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
3064 			else
3065 				CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0));
3066 		}
3067 
3068 		return;
3069 	}
3070 
3071 	// For lead actor, lock out the user (if not already locked out)
3072 	if (actor == GetLeadId() || actor == LEAD_ACTOR) {
3073 		_ctx->bTookControl = GetControl(CONTROL_OFFV2);
3074 		if (TinselV2 && _ctx->bTookControl)
3075 			RestoreMainCursor();
3076 	} else {
3077 		_ctx->bTookControl = false;
3078 	}
3079 
3080 	if (TinselV2 && (x2 == -1) && (y2 == -1)) {
3081 		// First co-ordinates are the destination
3082 		x2 = x1;
3083 		y2 = y1;
3084 	} else {
3085 		// Stand at the start co-ordinates
3086 		hPath = InPolygon(x1, y1, PATH);
3087 
3088 		if (hPath != NOPOLY) {
3089 			// Walking out of a path
3090 			CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
3091 		} else {
3092 			hPath = InPolygon(x2, y2, PATH);
3093 			// One of them has to be in a path
3094 			assert(hPath != NOPOLY); //one co-ordinate must be in a legal path
3095 
3096 			// Walking into a path
3097 			CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0));	// Get path's characteristics
3098 			CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
3099 		}
3100 
3101 		if (TinselV2 && (zOverride != -1)) {
3102 			PMOVER pMover = GetMover(actor);
3103 			assert(pMover);
3104 
3105 			SetMoverZ(pMover, y1, zOverride);
3106 		}
3107 	}
3108 
3109 	CORO_INVOKE_ARGS(Walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, zOverride, escOn, myEscape));
3110 
3111 	// Free control if we took it
3112 	if (_ctx->bTookControl)
3113 		Control(CONTROL_ON);
3114 
3115 	CORO_END_CODE;
3116 }
3117 
3118 /**
3119  * Gets a system variable
3120  */
SystemVar(int varId)3121 static int SystemVar(int varId) {
3122 	return SysVar(varId);
3123 }
3124 
3125 /**
3126  * Define a tagged actor.
3127  */
TagActor(int actor,SCNHANDLE text,int tp)3128 static void TagActor(int actor, SCNHANDLE text, int tp) {
3129 	Tag_Actor(actor, text, tp);
3130 }
3131 
3132 /**
3133  * TagPos([tag #])
3134  */
TagPos(MASTER_LIB_CODES operand,int tagno,HPOLYGON hp)3135 static int TagPos(MASTER_LIB_CODES operand, int tagno, HPOLYGON hp) {
3136 	int	x, y;
3137 
3138 	// Tag could be zero, meaning calling tag
3139 	if (tagno == 0)
3140 		tagno = GetTagPolyId(hp);
3141 
3142 	if (operand == TAGTAGXPOS || operand == TAGTAGYPOS) {
3143 		SCNHANDLE junk;
3144 
3145 		GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
3146 	} else {
3147 		GetPolyNode(GetTagHandle(tagno), &x, &y);
3148 	}
3149 
3150 	if (operand == TAGTAGXPOS || operand == TAGWALKXPOS)
3151 		return x;
3152 	else
3153 		return y;
3154 }
3155 
3156 /**
3157  * Text goes over actor's head while actor plays the talk reel.
3158  */
FinishTalkingReel(CORO_PARAM,PMOVER pMover,int actor)3159 static void FinishTalkingReel(CORO_PARAM, PMOVER pMover, int actor) {
3160 	CORO_BEGIN_CONTEXT;
3161 	CORO_END_CONTEXT(_ctx);
3162 
3163 	CORO_BEGIN_CODE(_ctx);
3164 
3165 	if (pMover) {
3166 		SetMoverStanding(pMover);
3167 		AlterMover(pMover, 0, AR_POPREEL);
3168 	} else {
3169 		SetActorTalking(actor, false);
3170 		CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false));
3171 	}
3172 
3173 	CORO_END_CODE;
3174 }
3175 
TalkOrSay(CORO_PARAM,SPEECH_TYPE speechType,SCNHANDLE hText,int x,int y,SCNHANDLE hFilm,int actorId,bool bSustain,bool escOn,int myEscape)3176 static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x, int y,
3177 					  SCNHANDLE hFilm, int actorId, bool bSustain, bool escOn, int myEscape) {
3178 	CORO_BEGIN_CONTEXT;
3179 		int		Loffset, Toffset;	// Top left of display
3180 		int		actor;			// The speaking actor
3181 		PMOVER	pActor;			// For moving actors
3182 		int		myLeftEvent;
3183 		int		escEvents;
3184 		int		ticks;
3185 		bool	bTookControl;	// Set if this function takes control
3186 		bool	bTookTags;		// Set if this function disables tags
3187 		OBJECT	*pText;			// text object pointer
3188 		bool	bSample;		// Set if a sample is playing
3189 		bool	bSamples;
3190 		bool	bTalkReel;		// Set while talk reel is playing
3191 		Audio::SoundHandle handle;
3192 		int	timeout;
3193 
3194 		SPEECH_TYPE whatSort;
3195 		TFTYPE	direction;
3196 		int sub;
3197 		int x, y;
3198 	CORO_END_CONTEXT(_ctx);
3199 
3200 	CORO_BEGIN_CODE(_ctx);
3201 
3202 	_ctx->whatSort = speechType;
3203 	_ctx->escEvents = myEscape;
3204 	_ctx->x = x;
3205 	_ctx->y = y;
3206 	_ctx->Loffset = 0;
3207 	_ctx->Toffset = 0;
3208 	_ctx->ticks = 0;
3209 	_ctx->pText = NULL;
3210 
3211 	// If waiting is enabled, wait for ongoing scroll
3212 	if (TinselV2 && SysVar(SV_SPEECHWAITS))
3213 		CORO_INVOKE_1(WaitScroll, myEscape);
3214 
3215 	// Don't do it if it's not wanted
3216 	if (escOn && myEscape != GetEscEvents())
3217 		return;
3218 
3219 	_ctx->myLeftEvent = GetLeftEvents();
3220 
3221 	// If this actor is dead, call a stop to the calling process
3222 	if (!TinselV2 && (actorId && !actorAlive(actorId)))
3223 		CORO_KILL_SELF();
3224 
3225 	if (!TinselV2 || (speechType == IS_TALK)) {
3226 		/*
3227 		 * Find out which actor is talking
3228 		 * and with which direction if no film supplied
3229 		 */
3230 		switch (hFilm) {
3231 		case TF_NONE:
3232 		case TF_UP:
3233 		case TF_DOWN:
3234 		case TF_LEFT:
3235 		case TF_RIGHT:
3236 			_ctx->actor = GetLeadId();	// If no film, actor is lead actor
3237 			_ctx->direction = (TFTYPE)hFilm;
3238 			break;
3239 
3240 		default:
3241 			_ctx->actor = ExtractActor(hFilm);
3242 			assert(_ctx->actor); // talk() - no actor ID in the reel
3243 			_ctx->direction = TF_FILM;
3244 			break;
3245 		}
3246 		assert(_ctx->actor);
3247 	} else if (TinselV2)
3248 		_ctx->actor = actorId;
3249 
3250 	/*
3251 	 * Lock out the user (for lead actor, if not already locked out)
3252 	 * May need to disable tags for other actors
3253 	 */
3254 	if (_ctx->actor == GetLeadId() || (TinselV2 && (_ctx->actor == LEAD_ACTOR)))
3255 		_ctx->bTookControl = GetControl(CONTROL_OFF);
3256 	else
3257 		_ctx->bTookControl = false;
3258 	_ctx->bTookTags = DisableTagsIfEnabled();
3259 
3260 	if (TinselV2) {
3261 		/*
3262 		 * Divert stuff
3263 		 */
3264 		if (SysVar(ISV_DIVERT_ACTOR) && (_ctx->actor == GetLeadId() || _ctx->actor == LEAD_ACTOR)) {
3265 			_ctx->actor = SysVar(ISV_DIVERT_ACTOR);
3266 			if (_ctx->whatSort == IS_TALK)
3267 				_ctx->whatSort = IS_SAY;
3268 			else if (_ctx->whatSort == IS_TALKAT)
3269 				_ctx->whatSort = IS_SAYAT;
3270 		}
3271 	}
3272 
3273 	/*
3274 	 * Kick off the voice sample
3275 	 */
3276 	if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(hText)) {
3277 		if (!TinselV2) {
3278 			_vm->_sound->playSample(hText, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
3279 			_ctx->bSamples = _vm->_mixer->isSoundHandleActive(_ctx->handle);
3280 		} else
3281 			_ctx->bSamples = true;
3282 	} else
3283 		_ctx->bSamples = false;
3284 
3285 	/*
3286 	 * Replace actor with the talk reel, saving the current one
3287 	 */
3288 	_ctx->pActor = GetMover(_ctx->actor);
3289 	if (_ctx->whatSort == IS_TALK) {
3290 		if (_ctx->pActor) {
3291 			if (_ctx->direction != TF_FILM)
3292 				hFilm = GetMoverTalkReel(_ctx->pActor, _ctx->direction);
3293 			AlterMover(_ctx->pActor, hFilm, AR_PUSHREEL);
3294 		} else {
3295 			SetActorTalking(_ctx->actor, true);
3296 			SetActorTalkFilm(_ctx->actor, hFilm);
3297 			CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, -1, -1, 0, false, 0, escOn, myEscape, false));
3298 		}
3299 		_ctx->bTalkReel = true;
3300 		CORO_SLEEP(1);		// Allow the play to come in
3301 
3302 	} else if (_ctx->whatSort == IS_TALKAT) {
3303 		_ctx->bTalkReel = false;
3304 
3305 	} else if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
3306 		_ctx->bTalkReel = false;
3307 		if (IsTaggedActor(_ctx->actor)) {
3308 			CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, TALKING, false, 0));
3309 		} else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY)) {
3310 			CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, GetTagHandle(_ctx->actor | ACTORTAG_KEY),
3311 				TALKING, 0, false, 0));
3312 		}
3313 
3314 		if (TinselV2)
3315 			// Let it all kick in and position this 'waiting' process
3316 			// down the process list from the playing process(es)
3317 			// This ensures immediate return when the reel finishes
3318 			CORO_GIVE_WAY;
3319 	}
3320 
3321 	// Make multi-ones escape
3322 	if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->escEvents)
3323 		_ctx->escEvents = GetEscEvents();
3324 
3325 	for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
3326 		if (TinselV2 && _ctx->escEvents && _ctx->escEvents != GetEscEvents())
3327 			break;
3328 
3329 		/*
3330 		 * Display the text.
3331 		 */
3332 		_ctx->bSample = _ctx->bSamples;
3333 		_ctx->pText = NULL;
3334 
3335 		if (isJapanMode()) {
3336 			_ctx->ticks = JAP_TEXT_TIME;
3337 		} else if (_vm->_config->_useSubtitles || !_ctx->bSample) {
3338 			/*
3339 			 * Work out where to display the text
3340 			 */
3341 			int	xshift, yshift;
3342 
3343 			PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
3344 			if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK))
3345 				GetActorMidTop(_ctx->actor, &_ctx->x, &_ctx->y);
3346 
3347 			if (!TinselV0)
3348 				SetTextPal(GetActorRGB(_ctx->actor));
3349 			if (TinselV2)
3350 				LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
3351 			else {
3352 				LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
3353 
3354 				_ctx->y -= _ctx->Toffset;
3355 			}
3356 
3357 			_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
3358 					TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
3359 					GetTalkFontHandle(), TXT_CENTER);
3360 			assert(_ctx->pText); // talk() string produced NULL text;
3361 
3362 			if (IsTopWindow())
3363 				MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
3364 
3365 			if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK)) {
3366 				/*
3367 				 * Set bottom of text just above the speaker's head
3368 				 * But don't go off the top of the screen
3369 				 */
3370 				if (TinselV2)
3371 					MultiMoveRelXY(_ctx->pText, 0, _ctx->y - _ctx->Toffset - MultiLowest(_ctx->pText) - 2);
3372 				else {
3373 					yshift = _ctx->y - MultiLowest(_ctx->pText) - 2;		// Just above head
3374 					MultiMoveRelXY(_ctx->pText, 0, yshift);		//
3375 					yshift = MultiHighest(_ctx->pText);
3376 					if (yshift < 4)
3377 						MultiMoveRelXY(_ctx->pText, 0, 4 - yshift);	// Not off top
3378 
3379 					/*
3380 					 * Don't go off the side of the screen
3381 					 */
3382 					xshift = MultiRightmost(_ctx->pText) + 2;
3383 					if (xshift >= SCREEN_WIDTH)			// Not off right
3384 						MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
3385 					xshift = MultiLeftmost(_ctx->pText) - 1;
3386 					if (xshift <= 0)					// Not off left
3387 						MultiMoveRelXY(_ctx->pText, -xshift, 0);
3388 				}
3389 			}
3390 
3391 			if (TinselV2)
3392 				// Don't go off the screen
3393 				KeepOnScreen(_ctx->pText, &_ctx->x, &_ctx->y);
3394 
3395 			/*
3396 			 * Work out how long to talk.
3397 			 * During this time, reposition the text if the screen scrolls.
3398 			 */
3399 			_ctx->ticks = TextTime(TextBufferAddr());
3400 		}
3401 
3402 		if (TinselV2 && _ctx->bSample) {
3403 			// Kick off the sample now (perhaps with a delay)
3404 			if (g_bNoPause)
3405 				g_bNoPause = false;
3406 			else if (!TinselV2Demo)
3407 				CORO_SLEEP(SysVar(SV_SPEECHDELAY));
3408 
3409 			//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
3410 			_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
3411 		}
3412 
3413 		_ctx->timeout = SAMPLETIMEOUT;
3414 
3415 		do {
3416 			// Keep text in place if scrolling
3417 			if (_ctx->pText != NULL) {
3418 				int	nLoff, nToff;
3419 
3420 				PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
3421 				if (nLoff != _ctx->Loffset || nToff != _ctx->Toffset) {
3422 					MultiMoveRelXY(_ctx->pText, _ctx->Loffset - nLoff, _ctx->Toffset - nToff);
3423 					_ctx->Loffset = nLoff;
3424 					_ctx->Toffset = nToff;
3425 				}
3426 			}
3427 
3428 			CORO_SLEEP(1);
3429 
3430 			// Handle timeout decrementing and Escape presses
3431 			if (TinselV2) {
3432 				if ((_ctx->escEvents && _ctx->escEvents != GetEscEvents()) ||
3433 					(!bSustain && LeftEventChange(_ctx->myLeftEvent)) ||
3434 					(--_ctx->timeout <= 0)) {
3435 					// Left event only kills current sub-string
3436 					_ctx->myLeftEvent = GetLeftEvents();
3437 					break;
3438 				}
3439 			} else {
3440 				--_ctx->timeout;
3441 
3442 				// Abort if escapable and ESCAPE is pressed
3443 				// Abort if left click - hardwired feature for talk!
3444 				// Abort if sample times out
3445 				if ((escOn && myEscape != GetEscEvents())
3446 						|| (_ctx->myLeftEvent != GetLeftEvents())
3447 						|| (_ctx->timeout <= 0))
3448 					break;
3449 			}
3450 
3451 			if (_ctx->bSample) {
3452 				// Wait for sample to end whether or not
3453 				if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
3454 					if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
3455 						// No text or speed modification - just depends on sample
3456 						break;
3457 					} else {
3458 						// Talk reel stops at end of speech
3459 						if (!TinselV2 || (_ctx->bTalkReel && (_ctx->sub == SubStringCount(hText) - 1))) {
3460 							CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
3461 							_ctx->bTalkReel = false;
3462 						}
3463 						_ctx->bSample = false;
3464 					}
3465 				}
3466 
3467 				// Decrement the subtitles timeout counter
3468 				if (_ctx->ticks > 0) --_ctx->ticks;
3469 
3470 			} else {
3471 				// No sample - just depends on time
3472 				if (_ctx->ticks-- <= 0)
3473 					break;
3474 			}
3475 		} while (1);
3476 
3477 		if (_ctx->pText != NULL) {
3478 			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
3479 			_ctx->pText = NULL;
3480 		}
3481 		if (TinselV2 && _ctx->bSample)
3482 			_vm->_sound->stopSpecSample(hText, _ctx->sub);
3483 	}
3484 
3485 	/*
3486 	 * The talk is over now - dump the text
3487 	 * Stop the sample
3488 	 * Restore the actor's film or standing reel
3489 	 */
3490 	if (_ctx->bTalkReel)
3491 		CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
3492 	if (_ctx->pText != NULL)
3493 		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
3494 
3495 	if (TinselV2) {
3496 		if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
3497 			SetActorTalking(_ctx->actor, false);
3498 			if (IsTaggedActor(_ctx->actor))
3499 				CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, ENDTALK, false, 0));
3500 			else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY))
3501 				CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX,
3502 					GetTagHandle(_ctx->actor | ACTORTAG_KEY), ENDTALK, 0, false, 0));
3503 
3504 			CORO_SLEEP(1);
3505 		}
3506 	} else {
3507 		_vm->_mixer->stopHandle(_ctx->handle);
3508 	}
3509 
3510 	/*
3511 	 * Restore user control and tags, as appropriate
3512 	 * And, finally, release the talk token.
3513 	 */
3514 	if (_ctx->bTookControl) {
3515 		if (TinselV2) ControlOn(); else Control(CONTROL_ON);
3516 	}
3517 	if (_ctx->bTookTags)
3518 		EnableTags();
3519 
3520 	CORO_END_CODE;
3521 }
3522 
3523 /**
3524  * TalkAt(actor, x, y, text)
3525  */
TalkAt(CORO_PARAM,int actor,int x,int y,SCNHANDLE text,bool escOn,int myEscape)3526 static void TalkAt(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myEscape) {
3527 	if (!coroParam) {
3528 		// Don't do it if it's not wanted
3529 		if (escOn && myEscape != GetEscEvents())
3530 			return;
3531 
3532 		if (!isJapanMode() && (_vm->_config->_useSubtitles || !_vm->_sound->sampleExists(text)))
3533 			SetTextPal(GetActorRGB(actor));
3534 	}
3535 
3536 	Print(coroParam, x, y, text, 0, false, escOn, myEscape);
3537 }
3538 
3539 /**
3540  * TalkAtS(actor, x, y, text, sustain)
3541  */
TalkAtS(CORO_PARAM,int actor,int x,int y,SCNHANDLE text,int sustain,bool escOn,int myEscape)3542 static void TalkAtS(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myEscape) {
3543 	if (!coroParam) {
3544 		assert(sustain == 2);
3545 
3546 		// Don't do it if it's not wanted
3547 		if (escOn && myEscape != GetEscEvents())
3548 			return;
3549 
3550 		if (!isJapanMode())
3551 			SetTextPal(GetActorRGB(actor));
3552 	}
3553 
3554 	Print(coroParam, x, y, text, 0, sustain == 2, escOn, myEscape);
3555 }
3556 
3557 /**
3558  * Set talk font's palette entry.
3559  */
TalkAttr(int r1,int g1,int b1,bool escOn,int myEscape)3560 static void TalkAttr(int r1, int g1, int b1, bool escOn, int myEscape) {
3561 	if (isJapanMode())
3562 		return;
3563 
3564 	// Don't do it if it's not wanted
3565 	if (escOn && myEscape != GetEscEvents())
3566 		return;
3567 
3568 	if (r1 > MAX_INTENSITY)	r1 = MAX_INTENSITY;	// } Ensure
3569 	if (g1 > MAX_INTENSITY)	g1 = MAX_INTENSITY;	// } within limits
3570 	if (b1 > MAX_INTENSITY)	b1 = MAX_INTENSITY;	// }
3571 
3572 	SetTextPal(TINSEL_RGB(r1, g1, b1));
3573 }
3574 
3575 /**
3576  * TalkPaletteIndex
3577  */
TalkPaletteIndex(unsigned index)3578 static void TalkPaletteIndex(unsigned index) {
3579 	assert(index);
3580 
3581 	SetTalkTextOffset(index);
3582 }
3583 
3584 /**
3585  * Set talk font's palette entry.
3586  */
TalkRGB(COLORREF color,int myescEvent)3587 static void TalkRGB(COLORREF color, int myescEvent) {
3588 	// Don't do it if it's not wanted
3589 	if (myescEvent && myescEvent != GetEscEvents())
3590 		return;
3591 
3592 	SetTextPal(color);
3593 }
3594 
3595 /**
3596  * TalkVia("actor"/off)
3597  */
TalkVia(int actor)3598 static void TalkVia(int actor) {
3599 	SetSysVar(ISV_DIVERT_ACTOR, actor);
3600 }
3601 
3602 /**
3603  * Declare a temporary text font.
3604  */
TempTagFont(SCNHANDLE hFilm)3605 static void TempTagFont(SCNHANDLE hFilm) {
3606 	SetTempTagFontHandle(hFilm);	// Store the font handle
3607 }
3608 
3609 /**
3610  * Declare a temporary text font.
3611  */
TempTalkFont(SCNHANDLE hFilm)3612 static void TempTalkFont(SCNHANDLE hFilm) {
3613 	SetTempTalkFontHandle(hFilm);	// Store the font handle
3614 }
3615 
3616 /**
3617  * ThisObject
3618  */
ThisObject(INV_OBJECT * pinvo)3619 static int ThisObject(INV_OBJECT *pinvo) {
3620 	assert(pinvo != NULL);
3621 
3622 	return pinvo->id;
3623 }
3624 
3625 /**
3626  * ThisTag
3627  */
ThisTag(HPOLYGON hp)3628 static int ThisTag(HPOLYGON hp) {
3629 	int tagno;
3630 
3631 	assert(hp != NOPOLY);
3632 
3633 	tagno = GetTagPolyId(hp);
3634 
3635 	assert(IsTagPolygon(tagno));
3636 	assert(tagno);
3637 
3638 	return tagno;
3639 }
3640 
3641 /**
3642  * Get a timer's current count.
3643  */
TimerFn(int timerno)3644 static int TimerFn(int timerno) {
3645 	return Timer(timerno);
3646 }
3647 
3648 /**
3649  * Return the icon that caused the CONVERSE event.
3650  */
Topic()3651 int Topic() {
3652 	return GetIcon();
3653 }
3654 
3655 /**
3656  * topplay(film, x, y, actor, hold, complete)
3657  */
TopPlay(CORO_PARAM,SCNHANDLE film,int x,int y,int complete,int actorid,bool splay,int sfact,bool escOn,int myescTime)3658 static void TopPlay(CORO_PARAM, SCNHANDLE film, int x, int y, int complete, int actorid, bool splay, int sfact, bool escOn, int myescTime) {
3659 	Play(coroParam, film, x, y, complete, actorid, splay, sfact, escOn, myescTime, true);
3660 }
TopPlay(CORO_PARAM,SCNHANDLE hFilm,int x,int y,bool bComplete,int myescEvent,TINSEL_EVENT event)3661 static void TopPlay(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myescEvent, TINSEL_EVENT event) {
3662 	Play(coroParam, hFilm, x, y, bComplete, myescEvent, true, event, NOPOLY, 0);
3663 }
3664 
3665 /**
3666  * Open or close the 'top window'
3667  */
TopWindow(int bpos)3668 static void TopWindow(int bpos) {
3669 	bool isStart = (TinselV2 && (bpos != 0)) || (!TinselV2 && (bpos == TW_START));
3670 
3671 	KillInventory();
3672 
3673 	if (isStart)
3674 		OpenMenu(TOP_WINDOW);
3675 }
3676 
3677 /**
3678  * TranslucentIndex
3679  */
TranslucentIndex(unsigned index)3680 static void TranslucentIndex(unsigned index) {
3681 	assert(index <= 255);
3682 
3683 	SetTranslucencyOffset(index);
3684 }
3685 
3686 /**
3687  * Play a sample (DW1 only).
3688  */
TryPlaySample(CORO_PARAM,int sample,bool bComplete,bool escOn,int myEscape)3689 static void TryPlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
3690 	CORO_BEGIN_CONTEXT;
3691 	CORO_END_CONTEXT(_ctx);
3692 
3693 	CORO_BEGIN_CODE(_ctx);
3694 	// Don't do it if it's not appropriate
3695 	if (_vm->_sound->sampleIsPlaying()) {
3696 		// return, but prevent Glitter lock-up
3697 		CORO_SLEEP(1);
3698 		return;
3699 	}
3700 
3701 	CORO_INVOKE_ARGS(PlaySample, (CORO_SUBCTX, sample, bComplete, escOn, myEscape));
3702 	CORO_END_CODE;
3703 }
3704 
3705 /**
3706  * UnDimMusic
3707  */
UnDimMusic()3708 static void UnDimMusic() {
3709 	_vm->_pcmMusic->unDim(true);
3710 }
3711 
3712 /**
3713  * unhookscene
3714  */
UnHookSceneFn()3715 static void UnHookSceneFn() {
3716 	UnHookScene();
3717 }
3718 
3719 /**
3720  * Un-define an actor as tagged.
3721  */
UnTagActorFn(int actor)3722 static void UnTagActorFn(int actor) {
3723 	UnTagActor(actor);
3724 }
3725 
3726 /**
3727  * vibrate
3728  */
Vibrate()3729 static void Vibrate() {
3730 }
3731 
3732 /**
3733  * waitframe(int actor, int frameNumber)
3734  */
WaitFrame(CORO_PARAM,int actor,int frameNumber,bool escOn,int myEscape)3735 static void WaitFrame(CORO_PARAM, int actor, int frameNumber, bool escOn, int myEscape) {
3736 	CORO_BEGIN_CONTEXT;
3737 	CORO_END_CONTEXT(_ctx);
3738 
3739 	CORO_BEGIN_CODE(_ctx);
3740 
3741 	while (GetActorSteps(actor) < frameNumber) {
3742 		// Don't do it if it's not wanted
3743 		if (escOn && myEscape != GetEscEvents())
3744 			break;
3745 
3746 		CORO_SLEEP(1);
3747 	}
3748 
3749 	CORO_END_CODE;
3750 }
3751 
3752 /**
3753  * Return when a key pressed or button pushed.
3754  */
WaitKey(CORO_PARAM,bool escOn,int myEscape)3755 static void WaitKey(CORO_PARAM, bool escOn, int myEscape) {
3756 	CORO_BEGIN_CONTEXT;
3757 		int	startEvent;
3758 		int startX, startY;
3759 	CORO_END_CONTEXT(_ctx);
3760 
3761 	CORO_BEGIN_CODE(_ctx);
3762 
3763 	// Don't do it if it's not wanted
3764 	if (escOn && myEscape != GetEscEvents())
3765 		return;
3766 
3767 	for (;;) {
3768 		_ctx->startEvent = getUserEvents();
3769 		if (TinselV1) {
3770 			// Store cursor position
3771 			while (!GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false))
3772 				CORO_SLEEP(1);
3773 		}
3774 
3775 		while (_ctx->startEvent == getUserEvents()) {
3776 			CORO_SLEEP(1);
3777 
3778 			// Not necessary to monitor escape as it's an event anyway
3779 			if (TinselV1) {
3780 				int curX, curY;
3781 				GetCursorXY(&curX, &curY, false);	// Store cursor position
3782 				if (curX != _ctx->startX || curY != _ctx->startY)
3783 					break;
3784 			}
3785 
3786 			if (MenuActive())
3787 				break;
3788 		}
3789 
3790 		if (!MenuActive())
3791 			return;
3792 
3793 		do {
3794 			CORO_SLEEP(1);
3795 		} while (MenuActive());
3796 
3797 		CORO_SLEEP(ONE_SECOND / 2);		// Let it die down
3798 	}
3799 	CORO_END_CODE;
3800 }
3801 
3802 /**
3803  * Return when no scrolling is going on.
3804  */
WaitScroll(CORO_PARAM,int myescEvent)3805 void WaitScroll(CORO_PARAM, int myescEvent) {
3806 	CORO_BEGIN_CONTEXT;
3807 		int time;
3808 	CORO_END_CONTEXT(_ctx);
3809 
3810 	CORO_BEGIN_CODE(_ctx);
3811 
3812 	// wait for ongoing scroll
3813 	while (IsScrolling()) {
3814 		if (myescEvent && myescEvent != GetEscEvents())
3815 			break;
3816 
3817 		CORO_SLEEP(1);
3818 	}
3819 
3820 	CORO_END_CODE;
3821 }
3822 
3823 /**
3824  * Pause for requested time.
3825  */
WaitTime(CORO_PARAM,int time,bool frame,bool escOn,int myEscape)3826 static void WaitTime(CORO_PARAM, int time, bool frame, bool escOn, int myEscape) {
3827 	CORO_BEGIN_CONTEXT;
3828 		int time;
3829 	CORO_END_CONTEXT(_ctx);
3830 
3831 	CORO_BEGIN_CODE(_ctx);
3832 
3833 	// Don't do it if it's not wanted
3834 	if (escOn && myEscape != GetEscEvents())
3835 		return;
3836 
3837 	if (!frame)
3838 		time *= ONE_SECOND;
3839 
3840 	_ctx->time = time;
3841 	do {
3842 		CORO_SLEEP(1);
3843 
3844 		// Abort if escapable and ESCAPE is pressed
3845 		if (escOn && myEscape != GetEscEvents())
3846 			break;
3847 	} while (_ctx->time--);
3848 
3849 	CORO_END_CODE;
3850 }
3851 
3852 /**
3853  * Set a moving actor off on a walk.
3854  */
Walk(CORO_PARAM,int actor,int x,int y,SCNHANDLE hFilm,int hold,bool igPath,int zOverride,bool escOn,int myescEvent)3855 void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm, int hold, bool igPath,
3856 		  int zOverride, bool escOn, int myescEvent) {
3857 	CORO_BEGIN_CONTEXT;
3858 		int thisWalk;
3859 	CORO_END_CONTEXT(_ctx);
3860 
3861 	bool bQuick = hold != 0;
3862 	PMOVER pMover = GetMover(actor);
3863 
3864 	assert(pMover); // Can't walk a non-moving actor
3865 
3866 	CORO_BEGIN_CODE(_ctx);
3867 
3868 	// Straight there if escaped
3869 	if (escOn && myescEvent != GetEscEvents()) {
3870 		if (TinselV2)
3871 			StopMover(pMover);
3872 		CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
3873 		return;
3874 	}
3875 
3876 	if (TinselV2) {
3877 		if (MoverHidden(pMover))
3878 			return;
3879 
3880 		// Test 10/10/96
3881 		while (!MoverIs(pMover))
3882 			CORO_SLEEP(1);
3883 	}
3884 
3885 	assert(pMover->hCpath != NOPOLY); // moving actor not in path
3886 
3887 	// Croak if he is doing an SWalk()
3888 	if (TinselV2) {
3889 		// Croak if he is doing an SWalk()
3890 		if (MoverIsSWalking(pMover))
3891 			CORO_KILL_SELF();
3892 
3893 		_ctx->thisWalk = SetActorDest(pMover, x, y, igPath, hFilm);
3894 		SetMoverZoverride(pMover, zOverride);
3895 		DontScrollCursor();
3896 
3897 		if (!bQuick) {
3898 			while (MoverMoving(pMover)) {
3899 				// Straight there if escaped
3900 				if (escOn && myescEvent != GetEscEvents()) {
3901 					StopMover(pMover);
3902 					CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
3903 					break;
3904 				}
3905 
3906 				CORO_SLEEP(1);
3907 
3908 				// Die if superceded
3909 				if (_ctx->thisWalk != GetWalkNumber(pMover))
3910 					CORO_KILL_SELF();
3911 			}
3912 		}
3913 	} else {
3914 
3915 		GetToken(pMover->actorToken);
3916 		SetActorDest(pMover, x, y, igPath, hFilm);
3917 		DontScrollCursor();
3918 
3919 		if (hold == 2) {
3920 			;
3921 		} else {
3922 			while (MoverMoving(pMover)) {
3923 				CORO_SLEEP(1);
3924 
3925 				// Straight there if escaped
3926 				if (escOn && myescEvent != GetEscEvents()) {
3927 					CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
3928 					FreeToken(pMover->actorToken);
3929 					return;
3930 				}
3931 			}
3932 		}
3933 
3934 		FreeToken(pMover->actorToken);
3935 	}
3936 
3937 	CORO_END_CODE;
3938 }
3939 
3940 /**
3941  * Set a moving actor off on a walk.
3942  * Wait to see if its aborted or completed.
3943  */
Walked(CORO_PARAM,int actor,int x,int y,SCNHANDLE film,bool escOn,int myEscape,bool & retVal)3944 static void Walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myEscape, bool &retVal) {
3945 	// COROUTINE
3946 	CORO_BEGIN_CONTEXT;
3947 		int	thisWalk;
3948 	CORO_END_CONTEXT(_ctx);
3949 
3950 	PMOVER pMover = GetMover(actor);
3951 	assert(pMover); // Can't walk a non-moving actor
3952 
3953 	CORO_BEGIN_CODE(_ctx);
3954 
3955 	// Straight there if escaped
3956 	if (escOn && myEscape != GetEscEvents()) {
3957 		CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
3958 		retVal = true;
3959 		return;
3960 	}
3961 
3962 	if (TinselV2) {
3963 		if (MoverHidden(pMover) || !MoverIs(pMover)) {
3964 			retVal = false;
3965 			return;
3966 		}
3967 		assert(pMover->hCpath != NOPOLY); // moving actor not in path
3968 
3969 		// Not if he is doing an SWalk()
3970 		if (MoverIsSWalking(pMover)) {
3971 			retVal = false;
3972 			return;
3973 		}
3974 
3975 	} else {
3976 		// Pause before starting the walk
3977 		CORO_SLEEP(ONE_SECOND);
3978 
3979 		assert(pMover->hCpath != NOPOLY); // moving actor not in path
3980 
3981 		// Briefly aquire token to kill off any other normal walk
3982 		GetToken(pMover->actorToken);
3983 		FreeToken(pMover->actorToken);
3984 	}
3985 
3986 	_ctx->thisWalk = SetActorDest(pMover, x, y, false, film);
3987 	DontScrollCursor();
3988 
3989 	while (MoverMoving(pMover) && (_ctx->thisWalk == GetWalkNumber(pMover))) {
3990 		// Straight there if escaped
3991 		if (escOn && myEscape != GetEscEvents()) {
3992 			CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
3993 			retVal = true;
3994 			return;
3995 		}
3996 
3997 		CORO_SLEEP(1);
3998 	}
3999 
4000 	int	endx, endy;
4001 	GetMoverPosition(pMover, &endx, &endy);
4002 	retVal = (_ctx->thisWalk == GetWalkNumber(pMover) && endx == x && endy == y);
4003 
4004 	CORO_END_CODE;
4005 }
4006 
4007 /**
4008  * Declare a moving actor.
4009  */
WalkingActor(uint32 id,SCNHANDLE * rp=NULL)4010 static void WalkingActor(uint32 id, SCNHANDLE *rp = NULL) {
4011 	PMOVER	pActor;		// Moving actor structure
4012 
4013 	if (TinselVersion == TINSEL_V2) {
4014 		RegisterMover(id);
4015 		return;
4016 	}
4017 
4018 	RegisterMover(id);		// Establish as a moving actor
4019 	pActor = GetMover(id);
4020 	assert(pActor);
4021 
4022 	// Store all those reels
4023 	int i, j;
4024 	for (i = 0; i < 5; ++i) {
4025 		for (j = 0; j < 4; ++j)
4026 			pActor->walkReels[i][j] = *rp++;
4027 		for (j = 0; j < 4; ++j)
4028 			pActor->standReels[i][j] = *rp++;
4029 	}
4030 
4031 
4032 	for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
4033 		for (j = 0; j < 4; ++j) {
4034 			pActor->walkReels[i][j] = pActor->walkReels[4][j];
4035 			pActor->standReels[i][j] = pActor->standReels[2][j];
4036 		}
4037 	}
4038 }
4039 
4040 /**
4041  * Walk a moving actor towards the polygon's tag, but return when the
4042  * actor enters the polygon.
4043  */
WalkPoly(CORO_PARAM,int actor,SCNHANDLE film,HPOLYGON hp,bool escOn,int myEscape)4044 static void WalkPoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
4045 	int	pnodex, pnodey;
4046 
4047 	// COROUTINE
4048 	CORO_BEGIN_CONTEXT;
4049 		int thisWalk;
4050 	CORO_END_CONTEXT(_ctx);
4051 
4052 	assert(hp != NOPOLY); // WalkPoly() may only be called from a polygon code block
4053 	PMOVER pMover = GetMover(actor);
4054 	assert(pMover); // Can't walk a non-moving actor
4055 
4056 	CORO_BEGIN_CODE(_ctx);
4057 
4058 	// Straight there if escaped
4059 	if (escOn && myEscape != GetEscEvents()) {
4060 		StandTag(actor, hp);
4061 		return;
4062 	}
4063 
4064 	if (TinselV2) {
4065 		if (MoverHidden(pMover))
4066 			return;
4067 
4068 		// Croak if he is doing an SWalk()
4069 		if (MoverIsSWalking(pMover))
4070 			CORO_KILL_SELF();
4071 
4072 	} else {
4073 		GetToken(pMover->actorToken);
4074 	}
4075 
4076 	GetPolyNode(hp, &pnodex, &pnodey);
4077 	_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
4078 	DoScrollCursor();
4079 
4080 	while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) {
4081 		CORO_SLEEP(1);
4082 
4083 		if (escOn && myEscape != GetEscEvents()) {
4084 			// Straight there if escaped
4085 			StandTag(actor, hp);
4086 			if (!TinselV2)
4087 				FreeToken(pMover->actorToken);
4088 			return;
4089 		}
4090 
4091 		// Die if superceded
4092 		if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
4093 			CORO_KILL_SELF();
4094 	}
4095 
4096 	if (!TinselV2)
4097 		FreeToken(pMover->actorToken);
4098 
4099 	CORO_END_CODE;
4100 }
4101 
4102 /**
4103  * WalkTag(actor, reel, hold)
4104  */
WalkTag(CORO_PARAM,int actor,SCNHANDLE film,HPOLYGON hp,bool escOn,int myEscape)4105 static void WalkTag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
4106 	// COROUTINE
4107 	CORO_BEGIN_CONTEXT;
4108 		int thisWalk;
4109 	CORO_END_CONTEXT(_ctx);
4110 
4111 	PMOVER pMover = GetMover(actor);
4112 	assert(pMover); // Can't walk a non-moving actor
4113 
4114 	CORO_BEGIN_CODE(_ctx);
4115 
4116 	int	pnodex, pnodey;
4117 
4118 	assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block
4119 
4120 	// Straight there if escaped
4121 	if (escOn && myEscape != GetEscEvents()) {
4122 		StandTag(actor, hp);
4123 		return;
4124 	}
4125 
4126 	if (!TinselV2)
4127 		GetToken(pMover->actorToken);
4128 	else {
4129 		if (MoverHidden(pMover))
4130 			return;
4131 	}
4132 
4133 	GetPolyNode(hp, &pnodex, &pnodey);
4134 
4135 	_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
4136 	DoScrollCursor();
4137 
4138 	while (MoverMoving(pMover)) {
4139 		if (escOn && myEscape != GetEscEvents()) {
4140 			// Straight there if escaped
4141 			StandTag(actor, hp);
4142 			if (!TinselV2)
4143 				FreeToken(pMover->actorToken);
4144 			return;
4145 		}
4146 
4147 		CORO_SLEEP(1);
4148 
4149 		// Die if superceded
4150 		if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
4151 			CORO_KILL_SELF();
4152 	}
4153 
4154 	// Adopt the tag-related reel
4155 	SCNHANDLE pFilm = GetPolyFilm(hp);
4156 
4157 	switch (pFilm) {
4158 	case TF_NONE:
4159 		break;
4160 
4161 	case TF_UP:
4162 		SetMoverDirection(pMover, AWAY);
4163 		SetMoverStanding(pMover);
4164 		break;
4165 	case TF_DOWN:
4166 		SetMoverDirection(pMover, FORWARD);
4167 		SetMoverStanding(pMover);
4168 		break;
4169 	case TF_LEFT:
4170 		SetMoverDirection(pMover, LEFTREEL);
4171 		SetMoverStanding(pMover);
4172 		break;
4173 	case TF_RIGHT:
4174 		SetMoverDirection(pMover, RIGHTREEL);
4175 		SetMoverStanding(pMover);
4176 		break;
4177 
4178 	default:
4179 		if (actor == LEAD_ACTOR || actor == GetLeadId())
4180 			AlterMover(pMover, pFilm, AR_NORMAL);
4181 		else
4182 			SetMoverStanding(pMover);
4183 		break;
4184 	}
4185 
4186 	if (!TinselV2)
4187 		FreeToken(pMover->actorToken);
4188 
4189 	CORO_END_CODE;
4190 }
4191 
4192 /**
4193  * Returns the X co-ordinateof lead actor's last walk.
4194  */
WalkXPos()4195 int WalkXPos() {
4196 	return GetLastLeadXdest();
4197 }
4198 
4199 /**
4200  * Returns the Y co-ordinateof lead actor's last walk.
4201  */
WalkYPos()4202 int WalkYPos() {
4203 	return GetLastLeadYdest();
4204 }
4205 
4206 /**
4207  * Return which is the current CD, counting from 1.
4208  */
WhichCd()4209 int WhichCd() {
4210 	return GetCurrentCD();
4211 }
4212 
4213 /**
4214  * whichinventory
4215  */
WhichInventory()4216 int WhichInventory() {
4217 	return WhichInventoryOpen();
4218 }
4219 
4220 
4221 /**
4222  * Subtract one less that the number of parameters from pp
4223  * pp then points to the first parameter.
4224  *
4225  * If the library function has no return value:
4226  * return -(the number of parameters) to pop them from the stack
4227  *
4228  * If the library function has a return value:
4229  * return -(the number of parameters - 1) to pop most of them from
4230  * the stack, and stick the return value in pp[0]
4231  * @param operand			Library function
4232  * @param pp				Top of parameter stack
4233  */
CallLibraryRoutine(CORO_PARAM,int operand,int32 * pp,const INT_CONTEXT * pic,RESUME_STATE * pResumeState)4234 int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState) {
4235 	int libCode;
4236 	if (TinselV0) libCode = DW1DEMO_CODES[operand];
4237 	else if (!TinselV2) libCode = DW1_CODES[operand];
4238 	else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand];
4239 	else libCode = DW2_CODES[operand];
4240 
4241 	debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
4242 	switch (libCode) {
4243 	case ACTORATTR:
4244 		// DW1 only
4245 		pp -= 3;			// 4 parameters
4246 		ActorAttr(pp[0], pp[1], pp[2], pp[3]);
4247 		return -4;
4248 
4249 	case ACTORBRIGHTNESS:
4250 		// DW2 only
4251 		pp -= 1;
4252 		ActorBrightness(pp[0], pp[1]);
4253 		return -2;
4254 
4255 	case ACTORDIRECTION:
4256 		// Common to both DW1 & DW2
4257 		pp[0] = ActorDirection(pp[0]);
4258 		return 0;
4259 
4260 	case ACTORPALETTE:
4261 		// DW2 only
4262 		pp -= 2;			// 3 parameters
4263 		ActorPalette(pp[0], pp[1], pp[2]);
4264 		return -3;
4265 
4266 	case ACTORPRIORITY:
4267 		// DW2 only
4268 		pp -= 1;			// 2 parameters
4269 		ActorPriority(pp[0], pp[1]);
4270 		return -2;
4271 
4272 	case ACTORREF:
4273 		// Common to both DW1 & DW2
4274 		if (!TinselV0)
4275 			error("actorref isn't a real function");
4276 		return 0;
4277 
4278 	case ACTORRGB:
4279 		// DW2 only
4280 		pp -= 1;			// 2 parameters
4281 		ActorRGB(pp[0], pp[1]);
4282 		return -2;
4283 
4284 	case ACTORSCALE:
4285 		// Common to both DW1 & DW2
4286 		pp[0] = ActorScale(pp[0]);
4287 		return 0;
4288 
4289 	case ACTORSON:
4290 		// DW1 only
4291 		ActorsOn();
4292 		return 0;
4293 
4294 	case ACTORXPOS:
4295 		// Common to both DW1 & DW2
4296 		pp[0] = ActorPos(ACTORXPOS, pp[0]);
4297 		return 0;
4298 
4299 	case ACTORYPOS:
4300 		// Common to both DW1 & DW2
4301 		pp[0] = ActorPos(ACTORYPOS, pp[0]);
4302 		return 0;
4303 
4304 	case ADDHIGHLIGHT:
4305 		// DW2 only
4306 		// Command doesn't actually do anything
4307 		pp -= 1;			// 2 parameters
4308 		return -2;
4309 
4310 	case ADDINV:
4311 		// DW2 only
4312 		AddInv(INV_DEFAULT, pp[0]);
4313 		return -1;
4314 
4315 	case ADDINV1:
4316 		// Common to both DW1 & DW2
4317 		AddInv(INV_1, pp[0]);
4318 		return -1;
4319 
4320 	case ADDINV2:
4321 		// Common to both DW1 & DW2
4322 		AddInv(INV_2, pp[0]);
4323 		return -1;
4324 
4325 	case ADDOPENINV:
4326 		// Common to both DW1 & DW2
4327 		AddInv(TinselV2 ? DW2_INV_OPEN : INV_OPEN, pp[0]);
4328 		return -1;
4329 
4330 	case ADDTOPIC:
4331 		// Common to both DW1 & DW2
4332 		AddTopic(pp[0]);
4333 		return -1;
4334 
4335 	case AUXSCALE:
4336 		// DW1 only
4337 		pp -= 13;			// 14 parameters
4338 		AuxScale(pp[0], pp[1], (SCNHANDLE *)(pp+2));
4339 		return -14;
4340 
4341 	case BACKGROUND:
4342 		// Common to both DW1 & DW2
4343 		Background(coroParam, pp[0]);
4344 		return -1;
4345 
4346 	case BLOCKING:
4347 		// DW2 only
4348 		Blocking(pp[0]);
4349 		return -1;
4350 
4351 	case CALLACTOR:
4352 	case CALLGLOBALPROCESS:
4353 	case CALLOBJECT:
4354 		// DW2 only
4355 		pp -= 1;			// 2 parameters
4356 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
4357 			bool result;
4358 			*pResumeState = RES_NOT;
4359 			FinishWaiting(coroParam, pic, &result);
4360 			if (coroParam) {
4361 				*pResumeState = RES_1;
4362 				return 0;
4363 			}
4364 			pp[0] = result ? 1 : 0;
4365 		} else {
4366 			uint32 v;
4367 			if (libCode == CALLACTOR)
4368 				v = SendActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
4369 			else if (libCode == CALLGLOBALPROCESS)
4370 				v = SendGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
4371 			else
4372 				v = SendObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
4373 
4374 			if (coroParam)
4375 				return 0;
4376 			pp[0] = v;
4377 		}
4378 
4379 		if (!pp[0])
4380 			KillSelf(coroParam);
4381 		return -2;
4382 
4383 
4384 	case CALLPROCESS:
4385 		// DW2 only
4386 		pp -= 1;			// 2 parameters
4387 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
4388 			bool result;
4389 			*pResumeState = RES_NOT;
4390 			FinishWaiting(coroParam, pic, &result);
4391 			if (coroParam) {
4392 				*pResumeState = RES_1;
4393 				return 0;
4394 			}
4395 
4396 			pp[0] = result ? 1 : 0;
4397 		} else {
4398 			int result = SendProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
4399 			if (coroParam)
4400 				return 0;
4401 
4402 			pp[0] = result;
4403 		}
4404 		return -2;
4405 
4406 	case CALLSCENE:
4407 		// DW2 only
4408 		error("CallScene isn't a real function");
4409 
4410 	case CALLTAG:
4411 		// DW2 only
4412 		pp -= 1;			// 2 parameters
4413 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
4414 			bool result;
4415 			*pResumeState = RES_NOT;
4416 			FinishWaiting(coroParam, pic, &result);
4417 			if (coroParam) {
4418 				*pResumeState = RES_1;
4419 				return 0;
4420 			}
4421 
4422 			pp[0] = result ? 1 : 0;
4423 		} else {
4424 			bool result;
4425 			SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
4426 			if (coroParam)
4427 				return 0;
4428 
4429 			pp[0] = result ? 1 : 0;
4430 		}
4431 
4432 		if (!pp[0])
4433 			KillSelf(coroParam);
4434 		return -2;
4435 
4436 	case CAMERA:
4437 		// Common to both DW1 & DW2
4438 		Camera(pp[0]);
4439 		return -1;
4440 
4441 	case CDCHANGESCENE:
4442 		// DW2 only
4443 		CdChangeScene(pp[0]);
4444 		return -1;
4445 
4446 	case CDDOCHANGE:
4447 		// DW2 only
4448 		CdDoChange(coroParam);
4449 		return 0;
4450 
4451 	case CDENDACTOR:
4452 		// DW2 only
4453 		CdEndActor(pp[0], pic->myEscape);
4454 		return -1;
4455 
4456 	case CDLOAD:
4457 		// Common to both DW1 & DW2
4458 		pp -= 1;			// 2 parameters
4459 		CDload(pp[0], pp[1], pic->myEscape);
4460 		return -2;
4461 
4462 	case CDPLAY:
4463 		// Common to both DW1 & DW2
4464 		error("cdplay isn't a real function");
4465 
4466 	case CLEARHOOKSCENE:
4467 		// Common to both DW1 & DW2
4468 		ClearHookScene();
4469 		return 0;
4470 
4471 	case CLOSEINVENTORY:
4472 		// Common to both DW1 & DW2
4473 		CloseInventory();
4474 		return 0;
4475 
4476 	case CONTROL:
4477 		// Common to both DW1 & DW2
4478 		Control(pp[0]);
4479 		return -1;
4480 
4481 	case CONVERSATION:
4482 		// Common to both DW1 & DW2
4483 		Conversation(coroParam, pp[0], pic->hPoly, pic->idActor, pic->escOn, pic->myEscape);
4484 		return -1;
4485 
4486 	case CONVTOPIC:
4487 		// Common to both DW1 & DW2
4488 		ConvTopic(pp[0]);
4489 		return -1;
4490 
4491 	case CURSOR:
4492 		// DW2 only
4493 		Cursor(pp[0]);
4494 		return -1;
4495 
4496 	case CURSORXPOS:
4497 		// Common to both DW1 & DW2
4498 		pp[0] = CursorPos(CURSORXPOS);
4499 		return 0;
4500 
4501 	case CURSORYPOS:
4502 		// Common to both DW1 & DW2
4503 		pp[0] = CursorPos(CURSORYPOS);
4504 		return 0;
4505 
4506 	case CUTSCENE:
4507 		// DW1 only
4508 		error("cutscene isn't a real function");
4509 
4510 	case DECCONVW:
4511 		// Common to both DW1 & DW2
4512 		pp -= 7;			// 8 parameters
4513 		DecConvW(pp[0], pp[1], pp[2], pp[3],
4514 			 pp[4], pp[5], pp[6], pp[7]);
4515 		return -8;
4516 
4517 	case DECCSTRINGS:
4518 		// DW1 only
4519 		pp -= 19;			// 20 parameters
4520 		DecCStrings((SCNHANDLE *)pp);
4521 		return -20;
4522 
4523 	case DECCURSOR:
4524 		// Common to both DW1 & DW2
4525 		DecCursor(pp[0]);
4526 		return -1;
4527 
4528 	case DECFLAGS:
4529 		// Common to both DW1 & DW2
4530 		if (TinselV2)
4531 			error("DecFlags() is obsolete");
4532 
4533 		DecFlags(pp[0]);
4534 		return -1;
4535 
4536 	case DECINV1:
4537 		// Common to both DW1 & DW2
4538 		pp -= 7;			// 8 parameters
4539 		DecInv1(pp[0], pp[1], pp[2], pp[3],
4540 			 pp[4], pp[5], pp[6], pp[7]);
4541 		return -8;
4542 
4543 	case DECINV2:
4544 		// Common to both DW1 & DW2
4545 		pp -= 7;			// 8 parameters
4546 		DecInv2(pp[0], pp[1], pp[2], pp[3],
4547 			 pp[4], pp[5], pp[6], pp[7]);
4548 		return -8;
4549 
4550 	case DECINVW:
4551 		// Common to both DW1 & DW2
4552 		DecInvW(pp[0]);
4553 		return -1;
4554 
4555 	case DECLARELANGUAGE:
4556 		// DW2 only
4557 		pp -= 2;			// 3 parameters
4558 		DeclareLanguage(pp[0], pp[1], pp[2]);
4559 		return -3;
4560 
4561 	case DECLEAD:
4562 		// Common to both DW1 & DW2
4563 		if (TinselV2) {
4564 			DecLead(pp[0]);
4565 			return -1;
4566 		} else {
4567 			pp -= 61;			// 62 parameters
4568 			DecLead(pp[0], (SCNHANDLE *)&pp[1], pp[61]);
4569 			return -62;
4570 		}
4571 
4572 	case DECSCALE:
4573 		// DW2 only
4574 		pp -= 13;			// 14 parameters
4575 		DecScale(pp[0], pp[1], pp[2], pp[3], pp[4],
4576 			 pp[5], pp[6], pp[7], pp[8], pp[9],
4577 			 pp[10], pp[11], pp[12], pp[13]);
4578 		return -14;
4579 
4580 	case DECTAGFONT:
4581 		// Common to both DW1 & DW2
4582 		DecTagFont(pp[0]);
4583 		return -1;
4584 
4585 	case DECTALKFONT:
4586 		// Common to both DW1 & DW2
4587 		DecTalkFont(pp[0]);
4588 		return -1;
4589 
4590 	case DELICON:
4591 		// DW1 only
4592 		DelIcon(pp[0]);
4593 		return -1;
4594 
4595 	case DELINV:
4596 		// DW1 only
4597 		DelInv(pp[0]);
4598 		return -1;
4599 
4600 	case DELTOPIC:
4601 		// DW2 only
4602 		DelTopic(pp[0]);
4603 		return -1;
4604 
4605 	case DIMMUSIC:
4606 		// DW2 only
4607 		DimMusic();
4608 		return 0;
4609 
4610 	case DROP:
4611 		// DW2 only
4612 		Drop(pp[0]);
4613 		return -1;
4614 
4615 	case DROPEVERYTHING:
4616 		// DW2 only
4617 		DropEverything();
4618 		return 0;
4619 
4620 	case DROPOUT:
4621 		// DW1 only
4622 		error("DropOut (%d)", pp[0]);
4623 
4624 	case EFFECTACTOR:
4625 		// Common to both DW1 & DW2
4626 		assert(pic->event == WALKIN || pic->event == WALKOUT); // effectactor() must be from effect poly code
4627 
4628 		pp[0] = pic->idActor;
4629 		return 0;
4630 
4631 	case ENABLEMENU:
4632 		// Common to both DW1 & DW2
4633 		EnableMenu();
4634 		return 0;
4635 
4636 	case ENDACTOR:
4637 		// DW2 only
4638 		EndActor(pp[0]);
4639 		return -1;
4640 
4641 	case ESCAPE:
4642 	case ESCAPEOFF:
4643 	case ESCAPEON:
4644 		// Common to both DW1 & DW2
4645 		error("Escape isn't a real function");
4646 
4647 	case EVENT:
4648 		// Common to both DW1 & DW2
4649 		if (TinselVersion == TINSEL_V2)
4650 			pp[0] = pic->event;
4651 		else
4652 			pp[0] = TINSEL1_EVENT_MAP[pic->event];
4653 		return 0;
4654 
4655 	case FACETAG:
4656 		// DW2 only
4657 		FaceTag(pp[0], pic->hPoly);
4658 		return -1;
4659 
4660 	case FADEIN:
4661 		// DW2 only
4662 		FadeIn();
4663 		return 0;
4664 
4665 	case FADEMIDI:
4666 		// DW1 only
4667 		FadeMidi(coroParam, pp[0]);
4668 		return -1;
4669 
4670 	case FADEOUT:
4671 		// DW1 only
4672 		FadeOut();
4673 		return 0;
4674 
4675 	case FRAMEGRAB:
4676 		// Common to both DW1 & DW2
4677 		return -1;
4678 
4679 	case FREEZECURSOR:
4680 		// DW2 only
4681 		FreezeCursor(pp[0]);
4682 		return -1;
4683 
4684 	case GETINVLIMIT:
4685 		// Common to both DW1 & DW2
4686 		pp[0] = GetInvLimit(pp[0]);
4687 		return 0;
4688 
4689 	case GHOST:
4690 		// DW2 only
4691 		pp -= 2;			// 3 parameters
4692 		Ghost(pp[0], pp[1], pp[2]);
4693 		return -3;
4694 
4695 	case GLOBALVAR:
4696 		// DW1 only
4697 		error("GlobalVar isn't a real function");
4698 
4699 	case GRABMOVIE:
4700 		// DW2 only
4701 		return -1;
4702 
4703 	case HAILSCENE:
4704 		// DW2 only
4705 		HailScene(pp[0]);
4706 		return -1;
4707 
4708 	case HASRESTARTED:
4709 		// Common to both DW1 & DW2
4710 		pp[0] = HasRestarted();
4711 		return 0;
4712 
4713 	case HAVE:
4714 		// DW2 only
4715 		pp[0] = Have(pp[0]);
4716 		return 0;			// using return value
4717 
4718 	case HELDOBJECT:
4719 		// Common to both DW1 & DW2
4720 		pp[0] = HeldObject();
4721 		return 0;
4722 
4723 	case HIDEACTOR:
4724 		// Common to both DW1 & DW2
4725 		if (!TinselV2)
4726 			HideActorFn(coroParam, pp[0]);
4727 		else if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
4728 			*pResumeState = RES_NOT;
4729 			FinishWaiting(coroParam, pic);
4730 			if (coroParam) {
4731 				*pResumeState = RES_1;
4732 				return 0;
4733 			}
4734 		} else
4735 			HideActorFn(coroParam, pp[0]);
4736 		return -1;
4737 
4738 	case HIDEBLOCK:
4739 		// DW2 only
4740 		HideBlock(pp[0]);
4741 		return -1;
4742 
4743 	case HIDEEFFECT:
4744 		// DW2 only
4745 		HideEffect(pp[0]);
4746 		return -1;
4747 
4748 	case HIDEPATH:
4749 		// DW2 only
4750 		HidePath(pp[0]);
4751 		return -1;
4752 
4753 	case HIDEREFER:
4754 		// DW2 only
4755 		HideRefer(pp[0]);
4756 		return -1;
4757 
4758 	case HIDETAG:
4759 		// DW2 only
4760 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
4761 			*pResumeState = RES_NOT;
4762 			FinishWaiting(coroParam, pic);
4763 			if (coroParam) {
4764 				*pResumeState = RES_1;
4765 				return 0;
4766 			}
4767 		} else {
4768 			HideTag(coroParam, pp[0], pic->hPoly);
4769 			if (coroParam)
4770 				return 0;
4771 		}
4772 		return -1;
4773 
4774 	case HOLD:
4775 		// DW2 only
4776 		Hold(pp[0]);
4777 		return -1;
4778 
4779 	case HOOKSCENE:
4780 		// Common to both DW1 & DW2
4781 		pp -= 2;			// 3 parameters
4782 		HookScene(pp[0], pp[1], pp[2]);
4783 		return -3;
4784 
4785 	case IDLETIME:
4786 		// Common to both DW1 & DW2
4787 		pp[0] = IdleTime();
4788 		return 0;
4789 
4790 	case ININVENTORY:
4791 		// DW1 only
4792 		pp[0] = InInventory(pp[0]);
4793 		return 0;			// using return value
4794 
4795 	case INSTANTSCROLL:
4796 		// DW2 only
4797 		InstantScroll(pp[0]);
4798 		return -1;
4799 
4800 	case INVDEPICT:
4801 		// DW1 only
4802 		pp -= 1;			// 2 parameters
4803 		InvDepict(pp[0], pp[1]);
4804 		return -2;
4805 
4806 	case INVENTORY:
4807 		// Common to both DW1 & DW2
4808 		Inventory(pp[0], pic->escOn, pic->myEscape);
4809 		return -1;
4810 
4811 	case INVPLAY:
4812 		// DW2 only
4813 		pp -= 1;			// 2 parameters
4814 		InvPlay(pp[0], pp[1]);
4815 		return -2;
4816 
4817 	case INWHICHINV:
4818 		// Common to both DW1 & DW2
4819 		pp[0] = InWhichInv(pp[0]);
4820 		return 0;			// using return value
4821 
4822 	case KILLACTOR:
4823 		// DW1 only
4824 		if (TinselV2)
4825 			error("KillActor() was not expected to be required");
4826 
4827 		KillActor(pp[0]);
4828 		return -1;
4829 
4830 	case KILLBLOCK:
4831 		// DW1 only
4832 		KillBlock(pp[0]);
4833 		return -1;
4834 
4835 	case KILLEXIT:
4836 		// DW1 only
4837 		KillExit(pp[0]);
4838 		return -1;
4839 
4840 	case KILLGLOBALPROCESS:
4841 		// DW2 only
4842 		KillGlobalProcess(pp[0]);
4843 		return -1;
4844 
4845 	case KILLPROCESS:
4846 		// DW2 only
4847 		KillProcess(pp[0]);
4848 		return -1;
4849 
4850 	case KILLTAG:
4851 		// DW1 only
4852 		KillTag(coroParam, pp[0]);
4853 		return -1;
4854 
4855 	case LOCALVAR:
4856 		// DW2 only
4857 		error("LocalVar isn't a real function");
4858 
4859 	case MOVECURSOR:
4860 		// Common to both DW1 & DW2
4861 		pp -= 1;			// 2 parameters
4862 		MoveCursor(pp[0], pp[1]);
4863 		return -2;
4864 
4865 	case MOVETAG:
4866 		// DW2 only
4867 		pp -= 2;			// 3 parameters
4868 		MoveTag(pp[0], pp[1], pp[2], pic->hPoly);
4869 		return -3;
4870 
4871 	case MOVETAGTO:
4872 		// DW2 only
4873 		pp -= 2;			// 3 parameters
4874 		MoveTagTo(pp[0], pp[1], pp[2], pic->hPoly);
4875 		return -3;
4876 
4877 	case NEWSCENE:
4878 		// Common to both DW1 & DW2
4879 		pp -= 2;			// 3 parameters
4880 		if (*pResumeState == RES_2)
4881 			*pResumeState = RES_NOT;
4882 		else
4883 			NewScene(coroParam, pp[0], pp[1], pp[2]);
4884 		return -3;
4885 
4886 	case NOBLOCKING:
4887 		// Common to both DW1 & DW2
4888 		NoBlocking();
4889 		return 0;
4890 
4891 	case NOPAUSE:
4892 		// DW2 only
4893 		g_bNoPause = true;
4894 		return 0;
4895 
4896 	case NOSCROLL:
4897 		// Common to both DW1 & DW2
4898 		pp -= 3;			// 4 parameters
4899 		NoScroll(pp[0], pp[1], pp[2], pp[3]);
4900 		return -4;
4901 
4902 	case OBJECTHELD:
4903 		// DW1 only
4904 		ObjectHeld(pp[0]);
4905 		return -1;
4906 
4907 	case OFFSET:
4908 		// Common to both DW1 & DW2
4909 		if (TinselV2) {
4910 			pp -= 2;			// 2 parameters
4911 			Offset((EXTREME)pp[0], pp[1], pp[2]);
4912 			return -3;
4913 		} else {
4914 			pp -= 1;			// 2 parameters
4915 			Offset(EX_USEXY, pp[0], pp[1]);
4916 			return -2;
4917 		}
4918 
4919 	case OTHEROBJECT:
4920 		// DW2 only
4921 		pp[0] = OtherObject(pic->pinvo);
4922 		return 0;
4923 
4924 	case PAUSE:
4925 		// DW2 only
4926 		WaitTime(coroParam, 1, true, pic->escOn, pic->myEscape);
4927 		return 0;
4928 
4929 	case PLAY:
4930 		// Common to both DW1 & DW2
4931 		if (TinselV2) {
4932 			pp -= 3;			// 4 parameters
4933 			if (*pResumeState == RES_1 && IsCdPlayHandle(pp[0]))
4934 				*pResumeState = RES_NOT;
4935 			else {
4936 				Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false,
4937 						pic->event, pic->hPoly, pic->idActor);
4938 			}
4939 			return -4;
4940 
4941 		} else {
4942 			pp -= 5;			// 6 parameters
4943 
4944 			if (pic->event == WALKIN || pic->event == WALKOUT)
4945 				Play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myEscape, false);
4946 			else
4947 				Play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape, false);
4948 			return -6;
4949 		}
4950 
4951 	case PLAYMIDI:
4952 		// Common to both DW1 & DW2
4953 		pp -= 2;			// 3 parameters
4954 		PlayMidi(coroParam, pp[0], pp[1], pp[2]);
4955 		return -3;
4956 
4957 	case PLAYMOVIE:
4958 		// DW2 only
4959 		PlayMovie(coroParam, pp[0], pic->myEscape);
4960 		return -1;
4961 
4962 	case PLAYMUSIC:
4963 		// DW2 only
4964 		PlayMusic(pp[0]);
4965 		return -1;
4966 
4967 	case PLAYRTF:
4968 		// Common to both DW1 & DW2
4969 		error("playrtf only applies to cdi");
4970 
4971 	case PLAYSAMPLE:
4972 		// Common to both DW1 & DW2
4973 		if (TinselV2) {
4974 			pp -= 3;			// 4 parameters
4975 			PlaySample(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape);
4976 			return -4;
4977 		} else {
4978 			pp -= 1;			// 2 parameters
4979 			PlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
4980 			return -2;
4981 		}
4982 
4983 	case POINTACTOR:
4984 		// DW2 only
4985 		PointActor(pp[0]);
4986 		return -1;
4987 
4988 	case POINTTAG:
4989 		// DW2 only
4990 		PointTag(pp[0], pic->hPoly);
4991 		return -1;
4992 
4993 	case POSTACTOR:
4994 		// DW2 only
4995 		pp -= 1;			// 2 parameters
4996 		PostActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->idActor, pic->myEscape);
4997 		return -2;
4998 
4999 	case POSTGLOBALPROCESS:
5000 		// DW2 only
5001 		pp -= 1;			// 2 parameters
5002 		PostGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
5003 		return -2;
5004 
5005 	case POSTOBJECT:
5006 		// DW2 only
5007 		pp -= 1;			// 2 parameters
5008 		PostObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
5009 		return -2;
5010 
5011 	case POSTPROCESS:
5012 		// DW2 only
5013 		pp -= 1;			// 2 parameters
5014 		PostProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
5015 		return -2;
5016 
5017 	case POSTTAG:
5018 		// DW2 only
5019 		pp -= 1;			// 2 parameters
5020 		PostTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
5021 		return -2;
5022 
5023 
5024 	case PREPARESCENE:
5025 		// DW1 only
5026 		PrepareScene(pp[0]);
5027 		return -1;
5028 
5029 	case PRINT:
5030 		// Common to both DW1 & DW2
5031 		if (TinselV2) {
5032 			pp -= 4;			// 5 parameters
5033 			Print(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4] != 0, pic->escOn, pic->myEscape);
5034 			return -5;
5035 		} else {
5036 			pp -= 5;			// 6 parameters
5037 			/* pp[2] was intended to be attribute */
5038 			Print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5] == 2, pic->escOn, pic->myEscape);
5039 			return -6;
5040 		}
5041 
5042 	case PRINTCURSOR:
5043 		// DW2 only
5044 		PrintTag(pic->hPoly, pp[0], pic->idActor, true);
5045 		return -1;
5046 
5047 	case PRINTOBJ:
5048 		// Common to both DW1 & DW2
5049 		PrintObj(coroParam, pp[0], pic->pinvo, pic->event, pic->myEscape);
5050 		return -1;
5051 
5052 	case PRINTTAG:
5053 		// Common to both DW1 & DW2
5054 		PrintTag(pic->hPoly, pp[0], TinselV2 ? pic->idActor : 0, false);
5055 		return -1;
5056 
5057 	case QUITGAME:
5058 		// Common to both DW1 & DW2
5059 		QuitGame();
5060 		return 0;
5061 
5062 	case RANDOM:
5063 		// Common to both DW1 & DW2
5064 		pp -= 2;			// 3 parameters
5065 		pp[0] = RandomFn(pp[0], pp[1], pp[2]);
5066 		return -2;		// One holds return value
5067 
5068 	case RESETIDLETIME:
5069 		// Common to both DW1 & DW2
5070 		ResetIdleTime();
5071 		return 0;
5072 
5073 	case RESTARTGAME:
5074 		// Common to both DW1 & DW2
5075 		FnRestartGame();
5076 		return 0;
5077 
5078 	case RESTORESCENE:
5079 		// Common to both DW1 & DW2
5080 		if (TinselV2) {
5081 			RestoreScene(coroParam, (TRANSITS)pp[0]);
5082 			return -1;
5083 		} else {
5084 			RestoreScene(coroParam, TRANS_FADE);
5085 			return 0;
5086 		}
5087 
5088 	case RESTORE_CUT:
5089 		// DW1 only
5090 		RestoreScene(coroParam, TRANS_CUT);
5091 		return 0;
5092 
5093 	case RESUMELASTGAME:
5094 		// DW2 only
5095 		ResumeLastGame();
5096 		return 0;
5097 
5098 	case RUNMODE:
5099 		// Common to both DW1 & DW2
5100 		pp[0] = RunMode();
5101 		return 0;
5102 
5103 	case SAMPLEPLAYING:
5104 		// DW1 only
5105 		pp[0] = SamplePlaying(pic->escOn, pic->myEscape);
5106 		return 0;
5107 
5108 	case SAVESCENE:
5109 		// Common to both DW1 & DW2
5110 		if (*pResumeState == RES_1)
5111 			*pResumeState = RES_2;
5112 		else
5113 			SaveScene(coroParam);
5114 		return 0;
5115 
5116 	case SAY:
5117 		// DW2 only
5118 		pp -= 1;			// 2 parameters
5119 		TalkOrSay(coroParam, IS_SAY, pp[1], 0, 0, 0, pp[0], false, pic->escOn, pic->myEscape);
5120 		return -2;
5121 
5122 	case SAYAT:
5123 		// DW2 only
5124 		pp -= 4;			// 5 parameters
5125 		TalkOrSay(coroParam, IS_SAYAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
5126 		return -5;
5127 
5128 	case SCALINGREELS:
5129 		// Common to both DW1 & DW2
5130 		pp -= 6;			// 7 parameters
5131 		ScalingReels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
5132 		return -7;
5133 
5134 	case SCANICON:
5135 		// DW1 only
5136 		pp[0] = ScanIcon();
5137 		return 0;
5138 
5139 	case SCREENXPOS:
5140 		// Common to both DW1 & DW2
5141 		pp[0] = LToffset(SCREENXPOS);
5142 		return 0;
5143 
5144 	case SCREENYPOS:
5145 		// Common to both DW1 & DW2
5146 		pp[0] = LToffset(SCREENYPOS);
5147 		return 0;
5148 
5149 	case SCROLL:
5150 		// Common to both DW1 & DW2
5151 		if (TinselV2) {
5152 			pp -= 5;			// 6 parameters
5153 			Scroll(coroParam, (EXTREME)pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pic->escOn, pic->myEscape);
5154 			return -6;
5155 		} else {
5156 			pp -= 3;			// 4 parameters
5157 			Scroll(coroParam, EX_USEXY, pp[0], pp[1], pp[2], pp[2], pp[3], pic->escOn, pic->myEscape);
5158 			return -4;
5159 		}
5160 
5161 	case SCROLLPARAMETERS:
5162 		// DW2 only
5163 		pp -= 6;			// 7 parameters
5164 		ScrollParameters(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
5165 		return -7;
5166 
5167 	case SENDTAG:
5168 		// DW2 only
5169 		pp -= 1;			// 2 parameters
5170 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
5171 			bool result;
5172 			*pResumeState = RES_NOT;
5173 			FinishWaiting(coroParam, pic, &result);
5174 			if (coroParam) {
5175 				*pResumeState = RES_1;
5176 				return 0;
5177 			}
5178 			pp[0] = result ? 1 : 0;
5179 		} else {
5180 			bool result;
5181 			SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
5182 			if (coroParam)
5183 				return 0;
5184 
5185 			pp[0] = result;
5186 		}
5187 		return -1;
5188 
5189 
5190 	case SETACTOR:
5191 		// DW1 only
5192 		SetActor(pp[0]);
5193 		return -1;
5194 
5195 	case SETBLOCK:
5196 		// DW1 only
5197 		SetBlock(pp[0]);
5198 		return -1;
5199 
5200 	case SETEXIT:
5201 		// DW1 only
5202 		SetExit(pp[0]);
5203 		return -1;
5204 
5205 	case SETINVLIMIT:
5206 		// Common to both DW1 & DW2
5207 		pp -= 1;			// 2 parameters
5208 		SetInvLimit(pp[0], pp[1]);
5209 		return -2;
5210 
5211 	case SETINVSIZE:
5212 		// Common to both DW1 & DW2
5213 		pp -= 6;			// 7 parameters
5214 		SetInvSize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
5215 		return -7;
5216 
5217 	case SETLANGUAGE:
5218 		// Common to both DW1 & DW2
5219 		SetLanguage((LANGUAGE)pp[0]);
5220 		return -1;
5221 
5222 	case SETPALETTE:
5223 		// Common to both DW1 & DW2
5224 		if (TinselV2) {
5225 			// Note: Although DW2 introduces parameters for start and length, it doesn't use them
5226 			pp -= 2;
5227 			SetPalette(pp[0], pic->escOn, pic->myEscape);
5228 			return -3;
5229 		} else {
5230 			SetPalette(pp[0], pic->escOn, pic->myEscape);
5231 			return -1;
5232 		}
5233 
5234 	case SETSYSTEMSTRING:
5235 		// DW2 only
5236 		pp -= 1;				// 2 parameters
5237 		SetSystemString(pp[0], pp[1]);
5238 		return -2;
5239 
5240 	case SETSYSTEMVAR:
5241 		// DW1 only
5242 		pp -= 1;				// 2 parameters
5243 		SetSystemVar(pp[0], pp[1]);
5244 		return -2;
5245 
5246 	case SETTAG:
5247 		// DW1 only
5248 		SetTag(coroParam, pp[0]);
5249 		return -1;
5250 
5251 	case SETTIMER:
5252 		// DW1 only
5253 		pp -= 3;			// 4 parameters
5254 		SetTimer(pp[0], pp[1], pp[2], pp[3]);
5255 		return -4;
5256 
5257 	case SHELL:
5258 		// DW2 only
5259 		Shell(pp[0]);
5260 		return 0;
5261 
5262 	case SHOWACTOR:
5263 		// DW2 only
5264 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
5265 			*pResumeState = RES_NOT;
5266 			FinishWaiting(coroParam, pic);
5267 			if (coroParam) {
5268 				*pResumeState = RES_1;
5269 				return 0;
5270 			}
5271 		} else
5272 			ShowActorFn(coroParam, pp[0]);
5273 		return -1;
5274 
5275 	case SHOWBLOCK:
5276 		// DW2 only
5277 		ShowBlock(pp[0]);
5278 		return -1;
5279 
5280 	case SHOWEFFECT:
5281 		// DW2 only
5282 		ShowEffect(pp[0]);
5283 		return -1;
5284 
5285 	case SHOWMENU:
5286 		// DW2 only
5287 		ShowMenu();
5288 		return 0;
5289 
5290 	case SHOWPATH:
5291 		// DW2 only
5292 		ShowPath(pp[0]);
5293 		return -1;
5294 
5295 	case SHOWPOS:
5296 		// DW1 only
5297 #ifdef DEBUG
5298 		showpos();
5299 #endif
5300 		return 0;
5301 
5302 	case SHOWREFER:
5303 		// DW2 only
5304 		ShowRefer(pp[0]);
5305 		return -1;
5306 
5307 	case SHOWSTRING:
5308 #ifdef DEBUG
5309 		showstring();
5310 #endif
5311 		return 0;
5312 
5313 	case SHOWTAG:
5314 		// DW2 only
5315 		if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
5316 			*pResumeState = RES_NOT;
5317 			FinishWaiting(coroParam, pic);
5318 
5319 			if (coroParam) {
5320 				*pResumeState = RES_1;
5321 				return 0;
5322 			}
5323 		} else {
5324 			ShowTag(coroParam, pp[0], pic->hPoly);
5325 		}
5326 		return -1;
5327 
5328 	case SPLAY:
5329 		// DW1 only
5330 		pp -= 6;			// 7 parameters
5331 
5332 		if (pic->event == WALKIN || pic->event == WALKOUT)
5333 			SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myEscape);
5334 		else
5335 			SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], pic->idActor, pic->escOn, pic->myEscape);
5336 		return -7;
5337 
5338 	case STAND:
5339 		// Common to both DW1 & DW2
5340 		pp -= 3;			// 4 parameters
5341 		Stand(coroParam, pp[0], pp[1], pp[2], pp[3]);
5342 		return -4;
5343 
5344 	case STANDTAG:
5345 		// Common to both DW1 & DW2
5346 		StandTag(pp[0], pic->hPoly);
5347 		return -1;
5348 
5349 	case STARTGLOBALPROCESS:
5350 		// DW2 only
5351 		StartGlobalProcess(coroParam, pp[0]);
5352 		return -1;
5353 
5354 	case STARTPROCESS:
5355 		// DW2 only
5356 		StartProcess(coroParam, pp[0]);
5357 		return -1;
5358 
5359 	case STARTTIMER:
5360 		// DW2 only
5361 		pp -= 3;			// 4 parameters
5362 		StartTimerFn(pp[0], pp[1], pp[2], pp[3]);
5363 		return -4;
5364 
5365 	case STOPMIDI:
5366 		// DW1 only
5367 		StopMidiFn();
5368 		return 0;
5369 
5370 	case STOPSAMPLE:
5371 		// Common to both DW1 & DW2
5372 		if (TinselV2) {
5373 			StopSample(pp[0]);
5374 			return -1;
5375 		} else {
5376 			StopSample();
5377 			return 0;
5378 		}
5379 
5380 	case STOPWALK:
5381 		// Common to both DW1 & DW2 only
5382 		StopWalk(pp[0]);
5383 		return -1;
5384 
5385 	case SUBTITLES:
5386 		// Common to both DW1 & DW2
5387 		Subtitles(pp[0]);
5388 		return -1;
5389 
5390 	case SWALK:
5391 		// Common to both DW1 & DW2
5392 		pp -= 5;			// 6 parameters
5393 		Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], -1, pic->escOn, pic->myEscape);
5394 		return -6;
5395 
5396 	case SWALKZ:
5397 		// DW2 only
5398 		pp -= 6;			// 7 parameters
5399 		Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pic->escOn, pic->myEscape);
5400 		return -7;
5401 
5402 	case SYSTEMVAR:
5403 		// DW2 only
5404 		pp[0] = SystemVar(pp[0]);
5405 		return 0;
5406 
5407 	case TAGACTOR:
5408 		pp -= 2;			// 3 parameters
5409 		TagActor(pp[0], pp[1], pp[2]);
5410 		return -3;
5411 
5412 	case TAGTAGXPOS:
5413 	case TAGTAGYPOS:
5414 	case TAGWALKXPOS:
5415 	case TAGWALKYPOS:
5416 		// DW2 only
5417 		pp[0] = TagPos((MASTER_LIB_CODES)libCode, pp[0], pic->hPoly);
5418 		return 0;
5419 
5420 	case TALK:
5421 		// Common to both DW1 & DW2
5422 		pp -= 1;			// 2 parameters
5423 
5424 		if (TinselV2)
5425 			TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
5426 		else if (pic->event == WALKIN || pic->event == WALKOUT)
5427 			TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
5428 		else
5429 			TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], pic->idActor, false, pic->escOn, pic->myEscape);
5430 		return -2;
5431 
5432 	case TALKAT:
5433 		// Common to both DW1 & DW2
5434 		if (TinselV2) {
5435 			pp -= 4;			// 5 parameters
5436 			TalkOrSay(coroParam, IS_TALKAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
5437 			return -5;
5438 		} else {
5439 			pp -= 3;			// 4 parameters
5440 			TalkAt(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape);
5441 			return -4;
5442 		}
5443 
5444 	case TALKATS:
5445 		// DW1 only
5446 		pp -= 4;			// 5 parameters
5447 		TalkAtS(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pic->escOn, pic->myEscape);
5448 		return -5;
5449 
5450 	case TALKATTR:
5451 		// DW1 only
5452 		pp -= 2;			// 3 parameters
5453 		TalkAttr(pp[0], pp[1], pp[2], pic->escOn, pic->myEscape);
5454 		return -3;
5455 
5456 	case TALKPALETTEINDEX:
5457 		// DW1 only
5458 		TalkPaletteIndex(pp[0]);
5459 		return -1;
5460 
5461 	case TALKRGB:
5462 		// DW2 only
5463 		TalkRGB(pp[0], pic->myEscape);
5464 		return -3;
5465 
5466 	case TALKVIA:
5467 		// DW2 only
5468 		TalkVia(pp[0]);
5469 		return -1;
5470 
5471 	case TEMPTAGFONT:
5472 		// DW2 only
5473 		TempTagFont(pp[0]);
5474 		return -1;
5475 
5476 	case TEMPTALKFONT:
5477 		// DW2 only
5478 		TempTalkFont(pp[0]);
5479 		return -1;
5480 
5481 	case THISOBJECT:
5482 		// DW2 only
5483 		pp[0] = ThisObject(pic->pinvo);
5484 		return 0;
5485 
5486 	case THISTAG:
5487 		// DW2 only
5488 		pp[0] = ThisTag(pic->hPoly);
5489 		return 0;
5490 
5491 	case TIMER:
5492 		// Common to both DW1 & DW2
5493 		pp[0] = TimerFn(pp[0]);
5494 		return 0;
5495 
5496 	case TOPIC:
5497 		// DW2 only
5498 		pp[0] = Topic();
5499 		return 0;
5500 
5501 	case TOPPLAY:
5502 		// Common to both DW1 & DW2
5503 		if (TinselV2) {
5504 			pp -= 3;			// 4 parameters
5505 			TopPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, pic->event);
5506 			return -4;
5507 		} else {
5508 			pp -= 5;			// 6 parameters
5509 			TopPlay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape);
5510 			return -6;
5511 		}
5512 
5513 	case TOPWINDOW:
5514 		// Common to both DW1 & DW2
5515 		TopWindow(pp[0]);
5516 		return -1;
5517 
5518 	case TRANSLUCENTINDEX:
5519 		// DW2 only
5520 		TranslucentIndex(pp[0]);
5521 		return -1;
5522 
5523 	case TRYPLAYSAMPLE:
5524 		// DW1 only
5525 		pp -= 1;			// 2 parameters
5526 		TryPlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
5527 		return -2;
5528 
5529 	case UNDIMMUSIC:
5530 		// DW2 only
5531 		UnDimMusic();
5532 		return 0;
5533 
5534 	case UNHOOKSCENE:
5535 		// Common to both DW1 & DW2
5536 		UnHookSceneFn();
5537 		return 0;
5538 
5539 	case UNTAGACTOR:
5540 		// DW1 only
5541 		UnTagActorFn(pp[0]);
5542 		return -1;
5543 
5544 	case VIBRATE:
5545 		// DW1 only
5546 		Vibrate();
5547 		return 0;
5548 
5549 	case WAITFRAME:
5550 		// Common to both DW1 & DW2
5551 		pp -= 1;			// 2 parameters
5552 		WaitFrame(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
5553 		return -2;
5554 
5555 	case WAITKEY:
5556 		// Common to both DW1 & DW2
5557 		WaitKey(coroParam, pic->escOn, pic->myEscape);
5558 		return 0;
5559 
5560 	case WAITSCROLL:
5561 		// DW2 only
5562 		WaitScroll(coroParam, pic->myEscape);
5563 		return 0;
5564 
5565 	case WAITTIME:
5566 		// Common to both DW1 & DW2
5567 		pp -= 1;			// 2 parameters
5568 		WaitTime(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
5569 		if (!coroParam && (pic->hCode == 0x3007540) && (pic->resumeState == RES_2))
5570 			// FIXME: This is a hack to return control to the user after using the prunes in
5571 			// the DW1 Demo, since I can't figure out how it's legitimately done
5572 			Control(CONTROL_ON);
5573 
5574 		return -2;
5575 
5576 	case WALK:
5577 		// Common to both DW1 & DW2
5578 		pp -= 4;			// 5 parameters
5579 		Walk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], false, -1, pic->escOn, pic->myEscape);
5580 		return -5;
5581 
5582 	case WALKED: {
5583 		// Common to both DW1 & DW2
5584 		pp -= 3;			// 4 parameters
5585 		bool tmp = false;
5586 		Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
5587 		if (!coroParam) {
5588 			// Only write the result to the stack if walked actually completed running.
5589 			pp[0] = tmp;
5590 		}
5591 		}
5592 		return -3;
5593 
5594 	case WALKINGACTOR:
5595 		// Common to both DW1 & DW2
5596 		if (TinselV2) {
5597 			// DW2 doesn't use a second parameter to WalkingActor
5598 			WalkingActor(pp[0]);
5599 			return -1;
5600 		} else {
5601 			pp -= 40;			// 41 parameters
5602 			WalkingActor(pp[0], (SCNHANDLE *)&pp[1]);
5603 			return -41;
5604 		}
5605 
5606 	case WALKPOLY:
5607 		// Common to both DW1 & DW2
5608 		if (TinselV2) {
5609 			pp -= 1;			// 2 parameters
5610 			WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
5611 			return -2;
5612 		} else {
5613 			pp -= 2;			// 3 parameters
5614 			WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
5615 			return -3;
5616 		}
5617 
5618 	case WALKTAG:
5619 		// Common to both DW1 & DW2
5620 		if (TinselV2) {
5621 			pp -= 1;			// 2 parameters
5622 			WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
5623 			return -2;
5624 		} else {
5625 			pp -= 2;			// 3 parameters
5626 			WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
5627 			return -3;
5628 		}
5629 
5630 	case WALKXPOS:
5631 		// DW2 only
5632 		pp[0] = WalkXPos();
5633 		return 0;
5634 
5635 	case WALKYPOS:
5636 		// DW2 only
5637 		pp[0] = WalkYPos();
5638 		return 0;
5639 
5640 	case WHICHCD:
5641 		// DW2 only
5642 		pp[0] = WhichCd();
5643 		return 0;
5644 
5645 	case WHICHINVENTORY:
5646 		// Common to both DW1 & DW2
5647 		pp[0] = WhichInventory();
5648 		return 0;
5649 
5650 	case ZZZZZZ:
5651 		// DW2 only - dummy routine used during debugging
5652 		return -1;
5653 
5654 	default:
5655 		error("Unsupported library function");
5656 	}
5657 
5658 	//error("Can't possibly get here");
5659 }
5660 
5661 } // End of namespace Tinsel
5662