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