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