mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-14 13:50:13 +00:00
e2d1e88d6f
aquire -> acquire
6853 lines
168 KiB
C++
6853 lines
168 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Glitter library functions.
|
|
*
|
|
* In the main called only from PCODE.C
|
|
* Function names are the same as Glitter code function names.
|
|
*
|
|
* To ensure exclusive use of resources and exclusive control responsibilities.
|
|
*/
|
|
|
|
#define BODGE
|
|
|
|
#include "common/coroutines.h"
|
|
#include "tinsel/actors.h"
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/bmv.h"
|
|
#include "tinsel/config.h"
|
|
#include "tinsel/cursor.h"
|
|
#include "tinsel/drives.h"
|
|
#include "tinsel/dw.h"
|
|
#include "tinsel/events.h"
|
|
#include "tinsel/faders.h"
|
|
#include "tinsel/film.h"
|
|
#include "tinsel/font.h"
|
|
#include "tinsel/graphics.h"
|
|
#include "tinsel/handle.h"
|
|
#include "tinsel/dialogs.h"
|
|
#include "tinsel/mareels.h"
|
|
#include "tinsel/move.h"
|
|
#include "tinsel/multiobj.h"
|
|
#include "tinsel/music.h"
|
|
#include "tinsel/object.h"
|
|
#include "tinsel/palette.h"
|
|
#include "tinsel/pcode.h"
|
|
#include "tinsel/pid.h"
|
|
#include "tinsel/play.h"
|
|
#include "tinsel/polygons.h"
|
|
#include "tinsel/movers.h"
|
|
#include "tinsel/savescn.h"
|
|
#include "tinsel/sched.h"
|
|
#include "tinsel/scn.h"
|
|
#include "tinsel/scroll.h"
|
|
#include "tinsel/sound.h"
|
|
#include "tinsel/strres.h"
|
|
#include "tinsel/sysvar.h"
|
|
#include "tinsel/text.h"
|
|
#include "tinsel/timers.h" // For ONE_SECOND constant
|
|
#include "tinsel/tinlib.h"
|
|
#include "tinsel/tinsel.h"
|
|
#include "tinsel/token.h"
|
|
#include "tinsel/noir/notebook.h"
|
|
#include "tinsel/noir/sysreel.h"
|
|
|
|
#include "common/textconsole.h"
|
|
|
|
namespace Tinsel {
|
|
|
|
//----------------- EXTERNAL GLOBAL DATA --------------------
|
|
|
|
// In DOS_DW.C
|
|
extern bool g_bRestart; // restart flag - set to restart the game
|
|
extern bool g_bHasRestarted; // Set after a restart
|
|
|
|
// In PCODE.CPP
|
|
extern bool g_bNoPause;
|
|
|
|
// In DOS_MAIN.C
|
|
// TODO/FIXME: From dos_main.c: "Only used on PSX so far"
|
|
//int clRunMode = 0;
|
|
|
|
//----------------- EXTERNAL FUNCTIONS ---------------------
|
|
|
|
// in PDISPLAY.CPP
|
|
extern void EnableTags();
|
|
extern void DisableTags();
|
|
bool DisableTagsIfEnabled();
|
|
extern void setshowstring();
|
|
|
|
// in SAVELOAD.CPP
|
|
extern int NewestSavedGame();
|
|
|
|
// in SCENE.CPP
|
|
extern void setshowpos();
|
|
extern int g_sceneCtr;
|
|
|
|
// in TINSEL.CPP
|
|
extern void SetCdChangeScene(SCNHANDLE hScene);
|
|
extern void SetHookScene(SCNHANDLE scene, int entrance, int transition);
|
|
extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
|
|
extern void UnHookScene();
|
|
extern void SuspendHook();
|
|
extern void UnSuspendHook();
|
|
|
|
#ifdef BODGE
|
|
// In SCENE.CPP
|
|
SCNHANDLE GetSceneHandle();
|
|
#endif
|
|
|
|
//----------------- LOCAL DEFINES --------------------
|
|
|
|
#define JAP_TEXT_TIME (2*ONE_SECOND)
|
|
|
|
/*----------------------------------------------------------------------*\
|
|
|* Library Procedure and Function codes *|
|
|
\*----------------------------------------------------------------------*/
|
|
|
|
enum MASTER_LIB_CODES {
|
|
ACTORATTR, ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY, ACTORREF,
|
|
ACTORRGB, ACTORSCALE, ACTORSON, ACTORXPOS, ACTORYPOS, ADDHIGHLIGHT,
|
|
ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC, AUXSCALE, BACKGROUND, BLOCKING,
|
|
CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT, CALLPROCESS, CALLSCENE, CALLTAG,
|
|
CAMERA, CDCHANGESCENE, CDDOCHANGE, CDENDACTOR, CDLOAD, CDPLAY, CLEARHOOKSCENE,
|
|
CLOSEINVENTORY, CONTROL, CONVERSATION, CONVTOPIC, CURSOR, CURSORXPOS, CURSORYPOS,
|
|
CUTSCENE, DECCONVW, DECCSTRINGS, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
|
|
DECLARELANGUAGE, DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELICON,
|
|
DELINV, DELTOPIC, DIMMUSIC, DROP, DROPEVERYTHING, DROPOUT, EFFECTACTOR, ENABLEMENU,
|
|
ENDACTOR, ESCAPE, ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEMIDI,
|
|
FADEOUT, FRAMEGRAB, FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE, HAILSCENE,
|
|
HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
|
|
HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, ININVENTORY, INSTANTSCROLL, INVDEPICT,
|
|
INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLBLOCK, KILLEXIT, KILLGLOBALPROCESS,
|
|
KILLPROCESS, KILLTAG, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
|
|
NOBLOCKING, NOPAUSE, NOSCROLL, OBJECTHELD, OFFSET, OTHEROBJECT, PAUSE, PLAY, PLAYMIDI,
|
|
PLAYMOVIE, PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS,
|
|
POSTOBJECT, POSTPROCESS, POSTTAG, PREPARESCENE, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
|
|
QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE, RESTORE_CUT,
|
|
RESUMELASTGAME, RUNMODE, SAMPLEPLAYING, SAVESCENE, SAY, SAYAT, SCALINGREELS,
|
|
SCANICON, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
|
|
SENDOBJECT, SENDPROCESS, SENDTAG, SETACTOR, SETBLOCK, SETBRIGHTNESS, SETEXIT, SETINVLIMIT,
|
|
SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMREEL, SETSYSTEMSTRING, SETSYSTEMVAR,
|
|
SETTAG, SETTIMER, SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWMENU, SHOWPATH,
|
|
SHOWPOS, SHOWREFER, SHOWSTRING, SHOWTAG, SPLAY, STAND, STANDTAG, STARTGLOBALPROCESS,
|
|
STARTPROCESS, STARTTIMER, STOPMIDI, STOPSAMPLE, STOPWALK, SUBTITLES, SWALK, SWALKZ,
|
|
SYSTEMVAR, TAGACTOR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT,
|
|
TALKATS, TALKATTR, TALKPALETTEINDEX, TALKRGB, TALKVIA, TEMPTAGFONT, TEMPTALKFONT,
|
|
THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
|
|
TRYPLAYSAMPLE, UNDIMMUSIC, UNHOOKSCENE, UNTAGACTOR, VIBRATE, WAITFRAME, WAITKEY,
|
|
WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
|
|
WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY, ZZZZZZ, DEC3D, DECINVMAIN,
|
|
ADDNOTEBOOK, ADDINV3, ADDCONV, SET3DTEXTURE, FADEMUSIC, VOICEOVER, SETVIEW,
|
|
HELDOBJECTORTOPIC, BOOKADDHYPERLINK, OPENNOTEBOOK, NTBPOLYENTRY, NTBPOLYPREVPAGE,
|
|
NTBPOLYNEXTPAGE, CROSSCLUE, HIGHEST_LIBCODE
|
|
};
|
|
|
|
static const MASTER_LIB_CODES DW1DEMO_CODES[] = {
|
|
ACTORREF, ACTORXPOS, ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, AUXSCALE, BACKGROUND,
|
|
CAMERA, CONTROL, CONVERSATION, CONVTOPIC, HIGHEST_LIBCODE, CURSORXPOS, CURSORYPOS,
|
|
DECCONVW, DECCURSOR, DECTAGFONT, DECINVW, DECINV1, DECINV2, DECLEAD, DELICON,
|
|
DELINV, EVENT, HIGHEST_LIBCODE, HELDOBJECT, HIDEACTOR, ININVENTORY, HIGHEST_LIBCODE,
|
|
INVENTORY, HIGHEST_LIBCODE, KILLACTOR, KILLBLOCK, KILLTAG, SCREENXPOS,
|
|
HIGHEST_LIBCODE, MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, HIGHEST_LIBCODE,
|
|
PLAY, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ, PRINTTAG, RESTORESCENE, SAVESCENE,
|
|
SCANICON, SCROLL, SETACTOR, SETBLOCK, HIGHEST_LIBCODE, SETTAG, SETTIMER, SHOWPOS,
|
|
SPLAY, STAND, STANDTAG, STOPWALK, HIGHEST_LIBCODE, SWALK, TAGACTOR, TALK,
|
|
SCREENYPOS, UNTAGACTOR, VIBRATE, WAITKEY, WAITTIME, WALK, WALKINGACTOR, WALKPOLY,
|
|
WALKTAG, RANDOM, TIMER
|
|
};
|
|
|
|
static const MASTER_LIB_CODES DW1_CODES[] = {
|
|
ACTORATTR, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS,
|
|
ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE,
|
|
BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION,
|
|
CONVTOPIC, CURSORXPOS, CURSORYPOS, DECCONVW, DECCURSOR,
|
|
DECINV1, DECINV2, DECINVW, DECLEAD, DECTAGFONT,
|
|
DECTALKFONT, DELICON, DELINV, EFFECTACTOR, ESCAPE, EVENT,
|
|
GETINVLIMIT, HELDOBJECT, HIDEACTOR, ININVENTORY, INVDEPICT,
|
|
INVENTORY, KILLACTOR, KILLBLOCK, KILLEXIT, KILLTAG, SCREENXPOS,
|
|
MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, PAUSE,
|
|
PLAY, PLAYMIDI, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ,
|
|
PRINTTAG, RANDOM, RESTORESCENE, SAVESCENE, SCALINGREELS,
|
|
SCANICON, SCROLL, SETACTOR, SETBLOCK, SETEXIT, SETINVLIMIT,
|
|
SETPALETTE, SETTAG, SETTIMER, SHOWPOS, SHOWSTRING, SPLAY,
|
|
STAND, STANDTAG, STOPWALK, SWALK, TAGACTOR, TALK, TALKATTR, TIMER,
|
|
SCREENYPOS, TOPPLAY, TOPWINDOW, UNTAGACTOR, VIBRATE, WAITKEY,
|
|
WAITTIME, WALK, WALKED, WALKINGACTOR, WALKPOLY, WALKTAG,
|
|
WHICHINVENTORY, ACTORSON, CUTSCENE, HOOKSCENE, IDLETIME,
|
|
RESETIDLETIME, TALKAT, UNHOOKSCENE, WAITFRAME, DECCSTRINGS,
|
|
STOPMIDI, STOPSAMPLE, TALKATS, DECFLAGS, FADEMIDI,
|
|
CLEARHOOKSCENE, SETINVSIZE, INWHICHINV, NOBLOCKING,
|
|
SAMPLEPLAYING, TRYPLAYSAMPLE, ENABLEMENU, RESTARTGAME, QUITGAME,
|
|
FRAMEGRAB, PLAYRTF, CDPLAY, CDLOAD, HASRESTARTED, RESTORE_CUT,
|
|
RUNMODE, SUBTITLES, SETLANGUAGE,
|
|
HIGHEST_LIBCODE
|
|
};
|
|
|
|
static const MASTER_LIB_CODES DW2DEMO_CODES[] = {
|
|
ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
|
|
ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
|
|
ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
|
|
BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
|
|
CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
|
|
CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
|
|
CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
|
|
DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
|
|
DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
|
|
DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
|
|
ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
|
|
FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, HASRESTARTED,
|
|
HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
|
|
HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, INSTANTSCROLL,
|
|
INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLGLOBALPROCESS,
|
|
KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
|
|
NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET, OTHEROBJECT, PAUSE, PLAY,
|
|
PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR,
|
|
POSTGLOBALPROCESS, POSTOBJECT, POSTPROCESS, POSTTAG, PRINT,
|
|
PRINTCURSOR, PRINTOBJ, PRINTTAG, QUITGAME, RANDOM, RESETIDLETIME,
|
|
RESTARTGAME, RESTORESCENE, RUNMODE, SAVESCENE, SAY, SAYAT,
|
|
SCALINGREELS, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS,
|
|
SENDACTOR, SENDGLOBALPROCESS, SENDOBJECT, SENDPROCESS, SENDTAG,
|
|
SETBRIGHTNESS, SETINVLIMIT, SETINVSIZE, SETLANGUAGE, SETPALETTE,
|
|
SETSYSTEMSTRING, SETSYSTEMVAR, SHELL, SHOWACTOR, SHOWBLOCK,
|
|
SHOWEFFECT, SHOWPATH, SHOWREFER, SHOWTAG, STAND, STANDTAG,
|
|
STARTGLOBALPROCESS, STARTPROCESS, STARTTIMER, STOPWALK, SUBTITLES,
|
|
SWALK, SYSTEMVAR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS,
|
|
TALK, TALKAT, TALKPALETTEINDEX, TALKRGB, TALKVIA, THISOBJECT,
|
|
THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
|
|
UNDIMMUSIC, UNHOOKSCENE, WAITFRAME, WAITKEY, WAITSCROLL, WAITTIME,
|
|
WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
|
|
WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY,
|
|
HIGHEST_LIBCODE
|
|
};
|
|
|
|
static const MASTER_LIB_CODES DW2_CODES[] = {
|
|
ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
|
|
ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
|
|
ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
|
|
BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
|
|
CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
|
|
CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
|
|
CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
|
|
DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
|
|
DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
|
|
DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
|
|
ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
|
|
FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE,
|
|
HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT,
|
|
HIDEPATH, HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME,
|
|
INSTANTSCROLL, INVENTORY, INVPLAY, INWHICHINV, KILLACTOR,
|
|
KILLGLOBALPROCESS, KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG,
|
|
MOVETAGTO, NEWSCENE, NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET,
|
|
OTHEROBJECT, PAUSE, PLAY, PLAYMUSIC, PLAYRTF, PLAYSAMPLE,
|
|
POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS, POSTOBJECT,
|
|
POSTPROCESS, POSTTAG, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
|
|
QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE,
|
|
RUNMODE, SAVESCENE, SAY, SAYAT, SCALINGREELS, SCREENXPOS,
|
|
SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
|
|
SENDOBJECT, SENDPROCESS, SENDTAG, SETBRIGHTNESS, SETINVLIMIT,
|
|
SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMSTRING, SETSYSTEMVAR,
|
|
SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWPATH, SHOWREFER,
|
|
SHOWTAG, STAND, STANDTAG, STARTGLOBALPROCESS, STARTPROCESS,
|
|
STARTTIMER, STOPWALK, SUBTITLES, SWALK, SYSTEMVAR, TAGTAGXPOS,
|
|
TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT, TALKPALETTEINDEX,
|
|
TALKRGB, TALKVIA, THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY,
|
|
TOPWINDOW, TRANSLUCENTINDEX, UNDIMMUSIC, UNHOOKSCENE, WAITFRAME,
|
|
WAITKEY, WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG,
|
|
WALKINGACTOR, WALKPOLY, WALKTAG, WALKXPOS, WALKYPOS, WHICHCD,
|
|
WHICHINVENTORY, ZZZZZZ, SWALKZ, DROPEVERYTHING, BLOCKING, STOPSAMPLE,
|
|
CDENDACTOR, DECLARELANGUAGE, RESUMELASTGAME, SHOWMENU, TEMPTALKFONT,
|
|
TEMPTAGFONT, PLAYMOVIE, HAILSCENE, SETSYSTEMREEL,
|
|
HIGHEST_LIBCODE
|
|
};
|
|
|
|
//----------------- GLOBAL GLOBAL DATA --------------------
|
|
|
|
// These vars are reset upon engine destruction
|
|
|
|
bool g_bEnableMenu;
|
|
|
|
static bool g_bInstantScroll = false;
|
|
static bool g_bEscapedCdPlay = false;
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
// These vars are reset upon engine destruction
|
|
|
|
// Saved cursor co-ordinates for control(on) to restore cursor position
|
|
// as it was at control(off).
|
|
// They are global so that MoveCursor(..) has a net effect if it
|
|
// precedes control(on).
|
|
static int g_controlX = 0, g_controlY = 0;
|
|
|
|
static int g_offtype = 0; // used by Control()
|
|
static uint32 g_lastValue = 0; // used by RandomFn()
|
|
static int g_scrollNumber = 0; // used by scroll()
|
|
|
|
static bool g_bNotPointedRunning = false; // Used in Printobj and PrintObjPointed
|
|
|
|
//----------------- FORWARD REFERENCES --------------------
|
|
|
|
static int HeldObject();
|
|
static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape);
|
|
void ResetIdleTime();
|
|
static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result);
|
|
static void StandTag(int actor, HPOLYGON hp);
|
|
void StopMidiFn();
|
|
void StopSample(int sample = -1);
|
|
static void StopWalk(int actor);
|
|
static void WaitScroll(CORO_PARAM, int myescEvent);
|
|
void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath,
|
|
int zOverride, bool escOn, int myescTime);
|
|
|
|
//----------------- SUPPORT FUNCTIONS --------------------
|
|
|
|
void ResetVarsTinlib() {
|
|
g_bEnableMenu = false;
|
|
|
|
g_bInstantScroll = false;
|
|
g_bEscapedCdPlay = false;
|
|
g_controlX = 0;
|
|
g_controlY = 0;
|
|
|
|
g_offtype = 0;
|
|
g_lastValue = 0;
|
|
g_scrollNumber = 0;
|
|
|
|
g_bNotPointedRunning = false;
|
|
}
|
|
|
|
/**
|
|
* For ScrollScreen() and Offset(), work out top left for a
|
|
* given screen position.
|
|
*/
|
|
static void DecodeExtreme(EXTREME extreme, int *px, int *py) {
|
|
int Loffset, Toffset;
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
|
|
switch (extreme) {
|
|
case EX_BOTTOM:
|
|
*px = Loffset;
|
|
*py = _vm->_bg->BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_BOTTOMLEFT:
|
|
*px = 0;
|
|
*py = _vm->_bg->BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_BOTTOMRIGHT:
|
|
*px = _vm->_bg->BgWidth() - SCREEN_WIDTH;
|
|
*py = _vm->_bg->BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_LEFT:
|
|
*px = 0;
|
|
*py = Toffset;
|
|
break;
|
|
case EX_RIGHT:
|
|
*px = _vm->_bg->BgWidth() - SCREEN_WIDTH;
|
|
*py = Toffset;
|
|
break;
|
|
case EX_TOP:
|
|
*px = Loffset;
|
|
*py = 0;
|
|
break;
|
|
case EX_TOPLEFT:
|
|
*px = *py = 0;
|
|
break;
|
|
case EX_TOPRIGHT:
|
|
*px = _vm->_bg->BgWidth() - SCREEN_WIDTH;
|
|
*py = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void KillSelf(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
CORO_KILL_SELF();
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
struct SCROLL_MONITOR {
|
|
int x;
|
|
int y;
|
|
int thisScroll;
|
|
int myEscape;
|
|
};
|
|
|
|
/**
|
|
* Monitor a scrolling, allowing Escape to interrupt it
|
|
*/
|
|
static void ScrollMonitorProcess(CORO_PARAM, const void *param) {
|
|
int Loffset, Toffset;
|
|
const SCROLL_MONITOR *psm = (const SCROLL_MONITOR *)param;
|
|
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
do {
|
|
CORO_SLEEP(1);
|
|
|
|
// give up if have been superseded
|
|
if (psm->thisScroll != g_scrollNumber)
|
|
break;
|
|
|
|
// If ESCAPE is pressed...
|
|
if (psm->myEscape != GetEscEvents()) {
|
|
// Instant completion!
|
|
Offset(EX_USEXY, psm->x, psm->y);
|
|
break;
|
|
}
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
|
|
} while (Loffset != psm->x || Toffset != psm->y);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* NOT A LIBRARY FUNCTION
|
|
*
|
|
* Poke supplied color into the DAC queue.
|
|
*/
|
|
void SetTextPal(COLORREF col) {
|
|
SetTalkColorRef(col);
|
|
UpdateDACqueue(TalkColor(), col);
|
|
}
|
|
|
|
/**
|
|
* Work out a time depending on length of string and
|
|
* subtitle speed modification.
|
|
*/
|
|
static int TextTime(char *pTstring) {
|
|
if (_vm->_config->isJapanMode())
|
|
return JAP_TEXT_TIME;
|
|
else if (!_vm->_config->_textSpeed)
|
|
return strlen(pTstring) + ONE_SECOND;
|
|
else
|
|
return strlen(pTstring) + ONE_SECOND + (_vm->_config->_textSpeed * 5 * ONE_SECOND) / 100;
|
|
}
|
|
|
|
/**
|
|
* KeepOnScreen
|
|
*/
|
|
void KeepOnScreen(OBJECT * pText, int *pTextX, int *pTextY) {
|
|
int shift;
|
|
|
|
// Not off the left
|
|
shift = MultiLeftmost(pText);
|
|
if (shift < 0) {
|
|
MultiMoveRelXY(pText, - shift, 0);
|
|
*pTextX -= shift;
|
|
}
|
|
|
|
// Not off the right
|
|
shift = MultiRightmost(pText);
|
|
if (shift > SCREEN_WIDTH) {
|
|
MultiMoveRelXY(pText, SCREEN_WIDTH - shift, 0);
|
|
*pTextX += SCREEN_WIDTH - shift;
|
|
}
|
|
|
|
// Not off the top
|
|
shift = MultiHighest(pText);
|
|
if (shift < 0) {
|
|
MultiMoveRelXY(pText, 0, - shift);
|
|
*pTextY -= shift;
|
|
}
|
|
|
|
// Not off the bottom
|
|
shift = MultiLowest(pText);
|
|
if (shift > SCREEN_BOX_HEIGHT2) {
|
|
MultiMoveRelXY(pText, 0, SCREEN_BOX_HEIGHT2 - shift);
|
|
*pTextX += SCREEN_WIDTH - shift;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waits until the specified process is finished
|
|
*/
|
|
static void FinishWaiting(CORO_PARAM, const INT_CONTEXT *pic, bool *result = NULL) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
while (pic->resumeCode == RES_WAITING)
|
|
CORO_SLEEP(1);
|
|
|
|
if (result)
|
|
*result = pic->resumeCode == RES_FINISHED;
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void TinGetVersion(WHICH_VER which, char *buffer, int length) {
|
|
|
|
if (length > VER_LEN)
|
|
length = VER_LEN;
|
|
|
|
char *cptr = (char *)FindChunk(MASTER_SCNHANDLE, CHUNK_TIME_STAMPS);
|
|
|
|
switch (which) {
|
|
case VER_GLITTER:
|
|
memcpy(buffer, cptr, length);
|
|
break;
|
|
|
|
case VER_COMPILE:
|
|
memcpy(buffer, cptr + VER_LEN, length);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/********************************************************************\
|
|
|***** Library functions *****|
|
|
\********************************************************************/
|
|
|
|
/**
|
|
* Set actor's attributes.
|
|
* - currently only the text color.
|
|
*/
|
|
static void ActorAttr(int actor, int r1, int g1, int b1) {
|
|
_vm->_actor->storeActorAttr(actor, r1, g1, b1);
|
|
}
|
|
|
|
/**
|
|
* Behave as if actor has walked into a polygon with given brughtness.
|
|
*/
|
|
void ActorBrightness(int actor, int brightness) {
|
|
MOVER *pMover = GetMover(actor);
|
|
|
|
assert(pMover != NULL);
|
|
assert(brightness >= 0 && brightness <= 10);
|
|
|
|
MoverBrightness(pMover, brightness);
|
|
}
|
|
|
|
/**
|
|
* Return a moving actor's current direction.
|
|
*/
|
|
static int ActorDirection(int actor) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
return (int)GetMoverDirection(pMover);
|
|
}
|
|
|
|
/**
|
|
* Set actor's palette details for path brightnesses
|
|
*/
|
|
void ActorPalette(int actor, int startColor, int length) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
StoreMoverPalette(pMover, startColor, length);
|
|
}
|
|
|
|
/**
|
|
* Set actor's Z-factor.
|
|
*/
|
|
static void ActorPriority(int actor, int zFactor) {
|
|
_vm->_actor->SetActorZfactor(actor, zFactor);
|
|
}
|
|
|
|
/**
|
|
* Set actor's text color.
|
|
*/
|
|
static void ActorRGB(int actor, COLORREF color) {
|
|
_vm->_actor->SetActorRGB(actor, color);
|
|
}
|
|
|
|
/**
|
|
* Return the actor's scale.
|
|
*/
|
|
static int ActorScale(int actor) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
return (int)GetMoverScale(pMover);
|
|
}
|
|
|
|
/**
|
|
* Returns the x or y position of an actor.
|
|
*/
|
|
static int ActorPos(int xory, int actor) {
|
|
int x, y;
|
|
|
|
_vm->_actor->GetActorPos(actor, &x, &y);
|
|
return (xory == ACTORXPOS) ? x : y;
|
|
}
|
|
|
|
/**
|
|
* Make all actors alive at the start of each scene.
|
|
*/
|
|
static void ActorsOn() {
|
|
_vm->_actor->SetActorsOn();
|
|
}
|
|
|
|
/**
|
|
* Adds an icon to the conversation window.
|
|
*/
|
|
static void AddTopic(int icon) {
|
|
_vm->_dialogs->addToInventory(INV_CONV, icon, false);
|
|
}
|
|
|
|
/**
|
|
* Place the object in inventory 1 or 2.
|
|
*/
|
|
static void AddInv(int invno, int object) {
|
|
// illegal inventory number
|
|
assert(invno == INV_1 || invno == INV_2 || invno == INV_3 || invno == INV_OPEN || invno == INV_DEFAULT);
|
|
|
|
_vm->_dialogs->addToInventory(invno, object, false);
|
|
}
|
|
|
|
/**
|
|
* Define an actor's walk and stand reels for an auxilliary scale.
|
|
*/
|
|
static void AuxScale(int actor, int scale, SCNHANDLE *rp) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
int j;
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->walkReels[scale-1][j] = *rp++;
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->standReels[scale-1][j] = *rp++;
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->talkReels[scale-1][j] = *rp++;
|
|
}
|
|
|
|
/**
|
|
* Defines the background image for a scene.
|
|
*/
|
|
static void startBackground(CORO_PARAM, SCNHANDLE bfilm) {
|
|
_vm->_bg->StartupBackground(coroParam, bfilm);
|
|
}
|
|
|
|
/**
|
|
* Disable dynamic blocking for current scene.
|
|
*/
|
|
void Blocking(bool onOrOff) {
|
|
SetSysVar(ISV_NO_BLOCKING, !onOrOff);
|
|
}
|
|
|
|
/**
|
|
* Sets focus of the scroll process.
|
|
*/
|
|
static void Camera(int actor) {
|
|
_vm->_scroll->ScrollFocus(actor);
|
|
}
|
|
|
|
/**
|
|
* Sets the CD Change Scene
|
|
*/
|
|
|
|
static void CdChangeScene(SCNHANDLE hScene) {
|
|
SetCdChangeScene(hScene);
|
|
}
|
|
|
|
/**
|
|
* CdDoChange
|
|
*/
|
|
void CdDoChange(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (!GotoCD())
|
|
return;
|
|
|
|
CORO_INVOKE_0(CdCD);
|
|
|
|
CdHasChanged();
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* CdEndActor("actor")
|
|
*/
|
|
void CdEndActor(int actor, int myEscape) {
|
|
MOVER *pMover; // for if it's a moving actor
|
|
|
|
// Only do it if escaped!
|
|
if (myEscape && myEscape != GetEscEvents()) {
|
|
// End current graphic
|
|
_vm->_actor->dwEndActor(actor);
|
|
|
|
// un-hide movers
|
|
pMover = GetMover(actor);
|
|
if (pMover)
|
|
UnHideMover(pMover);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A CDPLAY() is imminent.
|
|
*/
|
|
static void CDload(SCNHANDLE start, SCNHANDLE next, int myEscape) {
|
|
assert(start && next && start != next); // cdload() fault
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (myEscape && myEscape != GetEscEvents()) {
|
|
g_bEscapedCdPlay = true;
|
|
return;
|
|
}
|
|
|
|
_vm->_handle->LoadExtraGraphData(start, next);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the hooked scene (if any)
|
|
*/
|
|
static void ClearHookScene() {
|
|
SetHookScene(0, 0, TRANS_DEF);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void CloseInventory() {
|
|
_vm->_dialogs->killInventory();
|
|
}
|
|
|
|
/**
|
|
* Turn off cursor and take control from player - and variations on the theme.
|
|
* OR Restore cursor and return control to the player.
|
|
*/
|
|
void Control(int param) {
|
|
if (TinselVersion >= 2) {
|
|
if (param)
|
|
ControlOn();
|
|
else {
|
|
ControlOff();
|
|
|
|
switch (_vm->_dialogs->whichInventoryOpen()) {
|
|
case INV_1:
|
|
case INV_2:
|
|
case INV_MENU:
|
|
_vm->_dialogs->killInventory();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Tinsel 1 handling code
|
|
g_bEnableMenu = false;
|
|
|
|
switch (param) {
|
|
case CONTROL_STARTOFF:
|
|
GetControlToken(); // Take control
|
|
DisableTags(); // Switch off tags
|
|
_vm->_cursor->DwHideCursor(); // Blank out cursor
|
|
g_offtype = param;
|
|
break;
|
|
|
|
case CONTROL_OFF:
|
|
case CONTROL_OFFV:
|
|
case CONTROL_OFFV2:
|
|
if (TestToken(TOKEN_CONTROL)) {
|
|
GetControlToken(); // Take control
|
|
|
|
DisableTags(); // Switch off tags
|
|
_vm->_cursor->GetCursorXYNoWait(&g_controlX, &g_controlY, true); // Store cursor position
|
|
|
|
// There may be a button timing out
|
|
GetToken(TOKEN_LEFT_BUT);
|
|
FreeToken(TOKEN_LEFT_BUT);
|
|
}
|
|
|
|
if (g_offtype == CONTROL_STARTOFF)
|
|
_vm->_cursor->GetCursorXYNoWait(&g_controlX, &g_controlY, true); // Store cursor position
|
|
|
|
g_offtype = param;
|
|
|
|
if (param == CONTROL_OFF)
|
|
_vm->_cursor->DwHideCursor(); // Blank out cursor
|
|
else if (param == CONTROL_OFFV) {
|
|
_vm->_cursor->UnHideCursor();
|
|
_vm->_cursor->FreezeCursor();
|
|
} else if (param == CONTROL_OFFV2) {
|
|
_vm->_cursor->UnHideCursor();
|
|
}
|
|
break;
|
|
|
|
case CONTROL_ON:
|
|
if (g_offtype != CONTROL_OFFV2 && g_offtype != CONTROL_STARTOFF)
|
|
_vm->_cursor->SetCursorXY(g_controlX, g_controlY); // ... where it was
|
|
|
|
FreeControlToken(); // Release control
|
|
|
|
if (!_vm->_dialogs->inventoryActive())
|
|
EnableTags(); // Tags back on
|
|
|
|
_vm->_cursor->RestoreMainCursor(); // Re-instate cursor...
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open or close the conversation window.
|
|
*/
|
|
static void Conversation(CORO_PARAM, int fn, HPOLYGON hp, int actor, bool escOn, int myEscape) {
|
|
assert(hp != NOPOLY); // conversation() must (currently) be called from a polygon code block
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (fn == CONV_END) {
|
|
// Close down conversation
|
|
_vm->_dialogs->closeDownConv();
|
|
} else if ((fn == CONV_TOP) || (fn == CONV_DEF) || (fn == CONV_BOTTOM)) {
|
|
// TOP of screen, Default (i.e. TOP of screen), or BOTTOM of screen
|
|
|
|
// If waiting is enabled, wait for ongoing scroll
|
|
if ((TinselVersion >= 2) && SysVar(SV_CONVERSATIONWAITS))
|
|
CORO_INVOKE_1(WaitScroll, myEscape);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
// Don't do it if already in a conversation
|
|
if (_vm->_dialogs->isConvWindow())
|
|
return;
|
|
|
|
_vm->_dialogs->killInventory();
|
|
|
|
if (TinselVersion >= 2) {
|
|
// If this is from a tag polygon, get the associated
|
|
// actor (the one the polygon is named after), if any.
|
|
if (!actor) {
|
|
actor = GetTagPolyId(hp);
|
|
if (actor & ACTORTAG_KEY)
|
|
actor &= ~ACTORTAG_KEY;
|
|
else
|
|
actor = 0;
|
|
}
|
|
|
|
// Top or bottom; tag polygon or tagged actor
|
|
_vm->_dialogs->setConvDetails((CONV_PARAM)fn, hp, actor);
|
|
} else {
|
|
_vm->_dialogs->convPos(fn);
|
|
_vm->_dialogs->convPoly(hp);
|
|
}
|
|
|
|
_vm->_dialogs->popUpInventory(INV_CONV); // Conversation window
|
|
_vm->_dialogs->convAction(INV_OPENICON); // CONVERSATION event
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Add icon to conversation window's permanent default list.
|
|
*/
|
|
static void ConvTopic(int icon) {
|
|
_vm->_dialogs->permaConvIcon(icon);
|
|
}
|
|
|
|
/**
|
|
* ToggleCursor(on/off)
|
|
*/
|
|
void ToggleCursor(int onoff) {
|
|
if (onoff) {
|
|
// Re-instate cursor
|
|
_vm->_cursor->UnHideCursor();
|
|
} else {
|
|
// Blank out cursor
|
|
_vm->_cursor->DwHideCursor();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the x or y position of the cursor.
|
|
*/
|
|
static int CursorPos(int xory) {
|
|
int x, y;
|
|
|
|
_vm->_cursor->GetCursorXY(&x, &y, true);
|
|
return (xory == CURSORXPOS) ? x : y;
|
|
}
|
|
|
|
/**
|
|
* Declare 3d model for an actor.
|
|
*/
|
|
void Dec3D(int ano, SCNHANDLE hModelName, SCNHANDLE hTextureName) {
|
|
MOVER* pMover = GetMover(ano);
|
|
assert(pMover != nullptr);
|
|
|
|
pMover->type = MOVER_3D;
|
|
pMover->hModelName = hModelName;
|
|
pMover->hTextureName = hTextureName;
|
|
|
|
// if (_hModelNameLoaded == 0) {
|
|
// _hModelNameLoaded = hModelName;
|
|
// const char* modelName = (const char *)_vm->_handle->LockMem(hModelName);
|
|
// const char* textureName = (const char *)_vm->_handle->LockMem(hTextureName);
|
|
// LoadModels(modelName, textureName);
|
|
// }
|
|
//assert(_hModelNameLoaded == hModelName);
|
|
}
|
|
|
|
/**
|
|
* Declare conversation window.
|
|
*/
|
|
static void DecConvW(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
|
|
_vm->_dialogs->idec_convw(text, MaxContents, MinWidth, MinHeight,
|
|
StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Declare config strings.
|
|
*/
|
|
static void DecCStrings(SCNHANDLE *tp) {
|
|
_vm->_dialogs->setConfigStrings(tp);
|
|
}
|
|
|
|
/**
|
|
* Declare cursor's reels.
|
|
*/
|
|
static void DecCursor(SCNHANDLE hFilm) {
|
|
_vm->_cursor->DwInitCursor(hFilm);
|
|
}
|
|
|
|
/**
|
|
* Declare the language flags.
|
|
*/
|
|
static void DecFlags(SCNHANDLE hFilm) {
|
|
_vm->_dialogs->setFlagFilms(hFilm);
|
|
}
|
|
|
|
/**
|
|
* Declare inventory 1's parameters.
|
|
*/
|
|
static void DecInv1(SCNHANDLE text, int MaxContents,
|
|
int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight,
|
|
int MaxWidth, int MaxHeight) {
|
|
_vm->_dialogs->idec_inv1(text, MaxContents, MinWidth, MinHeight,
|
|
StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Declare inventory 2's parameters.
|
|
*/
|
|
static void DecInv2(SCNHANDLE text, int MaxContents,
|
|
int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight,
|
|
int MaxWidth, int MaxHeight) {
|
|
_vm->_dialogs->idec_inv2(text, MaxContents, MinWidth, MinHeight,
|
|
StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Declare parameters of inventories 1, 3 and 4.
|
|
* Display loadingscreen (?).
|
|
* Takes 8 parameter, but uses only 2.
|
|
*/
|
|
static void DecInvMain(SCNHANDLE text, int MaxContents,
|
|
int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight,
|
|
int MaxWidth, int MaxHeight) {
|
|
_vm->_dialogs->idec_invMain(text, MaxContents);
|
|
}
|
|
|
|
/**
|
|
* Declare the bits that the inventory windows are constructed from.
|
|
*/
|
|
static void DecInvW(SCNHANDLE hf) {
|
|
_vm->_dialogs->setInvWinParts(hf);
|
|
}
|
|
|
|
/**
|
|
* DeclareLanguage
|
|
*/
|
|
static void DeclareLanguage(int languageId, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
|
|
LanguageFacts(languageId, hDescription, hFlagFilm);
|
|
}
|
|
|
|
/**
|
|
* Declare lead actor.
|
|
* @param id Actor Id
|
|
* @param rp Walk and stand reels for all the regular scales (v1 only)
|
|
* @param text Tag text (v1 only)
|
|
*/
|
|
static void DecLead(uint32 id, SCNHANDLE *rp = 0, SCNHANDLE text = 0) {
|
|
MOVER *pMover; // Moving actor structure
|
|
|
|
if (TinselVersion >= 2) {
|
|
// Tinsel 2 only specifies the lead actor Id
|
|
_vm->_actor->SetLeadId(id);
|
|
RegisterMover(id);
|
|
|
|
} else {
|
|
|
|
_vm->_actor->Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged
|
|
_vm->_actor->SetLeadId(id); // Establish this as the lead
|
|
RegisterMover(id); // Establish as a moving actor
|
|
|
|
pMover = GetMover(id); // Get moving actor structure
|
|
assert(pMover);
|
|
|
|
// Store all those reels
|
|
int i, j;
|
|
for (i = 0; i < 5; ++i) {
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->walkReels[i][j] = *rp++;
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->standReels[i][j] = *rp++;
|
|
for (j = 0; j < 4; ++j)
|
|
pMover->talkReels[i][j] = *rp++;
|
|
}
|
|
|
|
|
|
for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
|
|
for (j = 0; j < 4; ++j) {
|
|
pMover->walkReels[i][j] = pMover->walkReels[4][j];
|
|
pMover->standReels[i][j] = pMover->standReels[2][j];
|
|
pMover->talkReels[i][j] = pMover->talkReels[4][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* DecScale("actor", scale, 12*"reel")
|
|
* Define an actor's walk and stand reels for a scale.
|
|
*/
|
|
static void 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) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
SetWalkReels(pMover, scale, wkl, wkr, wkf, wka);
|
|
SetStandReels(pMover, scale, stl, str, stf, sta);
|
|
SetTalkReels(pMover, scale, tal, tar, taf, taa);
|
|
}
|
|
|
|
/**
|
|
* Remove an icon from the conversation window.
|
|
*/
|
|
static void DelIcon(int icon) {
|
|
_vm->_dialogs->remFromInventory(INV_CONV, icon);
|
|
}
|
|
|
|
/**
|
|
* Delete the object from inventory 1 or 2.
|
|
*/
|
|
static void DelInv(int object) {
|
|
if (!_vm->_dialogs->remFromInventory(INV_1, object)) // Remove from inventory 1...
|
|
_vm->_dialogs->remFromInventory(INV_2, object); // ...or 2 (whichever)
|
|
|
|
_vm->_dialogs->dropItem(object); // Stop holding it
|
|
}
|
|
|
|
/**
|
|
* DelTopic
|
|
*/
|
|
static void DelTopic(int icon) {
|
|
_vm->_dialogs->remFromInventory(INV_CONV, icon);
|
|
}
|
|
|
|
/**
|
|
* Delete the object from inventory 1 or 2.
|
|
*/
|
|
static void Drop(int object) {
|
|
if (object == -1)
|
|
object = HeldObject();
|
|
|
|
if (!_vm->_dialogs->remFromInventory(INV_1, object)) // Remove from inventory 1...
|
|
_vm->_dialogs->remFromInventory(INV_2, object); // ...or 2 (whichever)
|
|
|
|
_vm->_dialogs->dropItem(object); // Stop holding it
|
|
}
|
|
|
|
/**
|
|
* Delete all objects from inventory 1 and 2.
|
|
*/
|
|
static void DropEverything() {
|
|
_vm->_dialogs->holdItem(INV_NOICON, false);
|
|
|
|
_vm->_dialogs->clearInventory(INV_1);
|
|
_vm->_dialogs->clearInventory(INV_2);
|
|
}
|
|
|
|
/**
|
|
* EnableMenu
|
|
*/
|
|
static void EnableMenu() {
|
|
g_bEnableMenu = true;
|
|
}
|
|
|
|
/**
|
|
* Kill an actor's current graphics.
|
|
*/
|
|
static void EndActor(int actor) {
|
|
_vm->_actor->dwEndActor(actor);
|
|
}
|
|
|
|
/**
|
|
* Get the actor to look at the polygon.
|
|
* If the actor is at the tag, do a StandTag().
|
|
*/
|
|
static void FaceTag(int actor, HPOLYGON hp) {
|
|
MOVER *pMover; // Moving actor structure
|
|
int nowx, nowy;
|
|
int nodex, nodey;
|
|
|
|
assert(hp != NOPOLY);
|
|
|
|
/*
|
|
* Get which moving actor it is
|
|
*/
|
|
pMover = GetMover(actor);
|
|
assert(pMover);
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
|
|
/*
|
|
* Stop the actor
|
|
*/
|
|
StopWalk(actor);
|
|
|
|
/*
|
|
* Face the tag
|
|
*/
|
|
// See where node is and where actor is
|
|
GetPolyNode(hp, &nodex, &nodey);
|
|
_vm->_actor->GetActorPos(actor, &nowx, &nowy);
|
|
|
|
if (nowx == nodex && nowy == nodey) {
|
|
// Stood at the tag, don't face in silly direction
|
|
StandTag(actor, hp);
|
|
} else {
|
|
// Look towards polygon
|
|
GetPolyMidBottom(hp, &nodex, &nodey);
|
|
SetMoverDirection(pMover, GetDirection(nowx, nowy,
|
|
nodex, nodey,
|
|
GetMoverDirection(pMover),
|
|
NOPOLY, YB_X1_5));
|
|
SetMoverStanding(pMover);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FadeMidi(in/out)
|
|
*/
|
|
static void FadeMidi(CORO_PARAM, int inout) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
assert(inout == FM_IN || inout == FM_OUT);
|
|
|
|
// To prevent compiler complaining
|
|
if (inout == FM_IN || inout == FM_OUT)
|
|
CORO_SLEEP(1);
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Freeze the cursor, or not.
|
|
*/
|
|
static void FreezeCursor(bool bFreeze) {
|
|
_vm->_cursor->DoFreezeCursor(bFreeze);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static int GetInvLimit(int invno) {
|
|
return _vm->_dialogs->invGetLimit(invno);
|
|
}
|
|
|
|
/**
|
|
* Ghost
|
|
*/
|
|
static void Ghost(int actor, int tColor, int tPalOffset) {
|
|
SetSysVar(ISV_GHOST_ACTOR, actor);
|
|
SetSysVar(ISV_GHOST_COLOR, tColor);
|
|
SetSysVar(ISV_GHOST_BASE, tPalOffset);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
static void HailScene(SCNHANDLE scene) {
|
|
DoHailScene(scene);
|
|
}
|
|
|
|
/**
|
|
* Returns TRUE if the game has been restarted, FALSE if not.
|
|
*/
|
|
static bool HasRestarted() {
|
|
return g_bHasRestarted;
|
|
}
|
|
|
|
/**
|
|
* See if an object is in the inventory.
|
|
*/
|
|
int Have(int object) {
|
|
return (_vm->_dialogs->inventoryPos(object) != INV_NOICON);
|
|
}
|
|
|
|
/**
|
|
* Returns which object is currently held.
|
|
*/
|
|
static int HeldObject() {
|
|
return _vm->_dialogs->whichItemHeld();
|
|
}
|
|
|
|
/**
|
|
* Hides the specified actor
|
|
*/
|
|
static void HideActorFn(CORO_PARAM, int ano) {
|
|
HideActor(coroParam, ano);
|
|
}
|
|
|
|
/**
|
|
* Turn a blocking polygon off.
|
|
*/
|
|
static void HideBlock(int block) {
|
|
DisableBlock(block);
|
|
}
|
|
|
|
/**
|
|
* Turn an effect polygon off.
|
|
*/
|
|
static void HideEffect(int effect) {
|
|
DisableEffect(effect);
|
|
}
|
|
|
|
/**
|
|
* Turn a path polygon off.
|
|
*/
|
|
static void HidePath(int path) {
|
|
DisablePath(path);
|
|
}
|
|
|
|
/**
|
|
* Turn a refer polygon off.
|
|
*/
|
|
static void HideRefer(int refer) {
|
|
DisableRefer(refer);
|
|
}
|
|
|
|
/**
|
|
* Turn a tag polygon off.
|
|
*/
|
|
static void HideTag(CORO_PARAM, int tag, HPOLYGON hp) {
|
|
// Tag could be zero, meaning calling tag
|
|
DisableTag(coroParam, tag ? tag : GetTagPolyId(hp));
|
|
}
|
|
|
|
/**
|
|
* Hold the specified object.
|
|
*/
|
|
static void Hold(int object) {
|
|
_vm->_dialogs->holdItem(object, false);
|
|
}
|
|
|
|
/**
|
|
* HookScene(scene, entrance, transition)
|
|
*/
|
|
void HookScene(SCNHANDLE scene, int entrance, int transition) {
|
|
SetHookScene(scene, entrance, transition);
|
|
}
|
|
|
|
/**
|
|
* IdleTime
|
|
*/
|
|
static int IdleTime() {
|
|
// If control is off, system is not idle
|
|
if (!ControlIsOn()) {
|
|
// Player doesn't currently have control
|
|
ResetIdleTime();
|
|
|
|
return 0;
|
|
} else {
|
|
// Player has control - return time since last event
|
|
int x = getUserEventTime() / ONE_SECOND;
|
|
|
|
return x;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set flag if InstantScroll(on), reset if InstantScroll(off)
|
|
*/
|
|
void InstantScroll(int onoff) {
|
|
g_bInstantScroll = (onoff != 0);
|
|
}
|
|
|
|
/**
|
|
* invdepict
|
|
*/
|
|
static void InvDepict(int object, SCNHANDLE hFilm) {
|
|
_vm->_dialogs->setObjectFilm(object, hFilm);
|
|
}
|
|
|
|
/**
|
|
* See if an object is in the inventory.
|
|
*/
|
|
int InInventory(int object) {
|
|
return (_vm->_dialogs->inventoryPos(object) != INV_NOICON);
|
|
}
|
|
|
|
/**
|
|
* Open an inventory.
|
|
*/
|
|
static void Inventory(int invno, bool escOn, int myEscape) {
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
assert((invno == INV_1 || invno == INV_2)); // Trying to open illegal inventory
|
|
|
|
_vm->_dialogs->popUpInventory(invno);
|
|
}
|
|
|
|
/**
|
|
* Alter inventory object's icon.
|
|
*/
|
|
static void InvPlay(int object, SCNHANDLE hFilm) {
|
|
_vm->_dialogs->setObjectFilm(object, hFilm);
|
|
}
|
|
|
|
/**
|
|
* See if an object is in the inventory.
|
|
*/
|
|
static int InWhichInv(int object) {
|
|
if (_vm->_dialogs->whichItemHeld() == object)
|
|
return 0;
|
|
|
|
if (_vm->_dialogs->isInInventory(object, INV_1))
|
|
return 1;
|
|
|
|
if (_vm->_dialogs->isInInventory(object, INV_2))
|
|
return 2;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Kill an actor.
|
|
*/
|
|
static void KillActor(int actor) {
|
|
_vm->_actor->DisableActor(actor);
|
|
}
|
|
|
|
/**
|
|
* Turn a blocking polygon off.
|
|
*/
|
|
static void KillBlock(int block) {
|
|
DisableBlock(block);
|
|
}
|
|
|
|
/**
|
|
* Turn an exit off.
|
|
*/
|
|
static void KillExit(int exit) {
|
|
DisableExit(exit);
|
|
}
|
|
|
|
/**
|
|
* Turn a tag off.
|
|
*/
|
|
static void KillTag(CORO_PARAM, int tagno) {
|
|
DisableTag(coroParam, tagno);
|
|
}
|
|
|
|
/**
|
|
* Kills the specified global process
|
|
*/
|
|
static void KillGlobalProcess(uint32 procID) {
|
|
xKillGlobalProcess(procID);
|
|
}
|
|
|
|
/**
|
|
* Kills the specified process
|
|
*/
|
|
static void KillProcess(uint32 procID) {
|
|
KillSceneProcess(procID);
|
|
}
|
|
|
|
/**
|
|
* Returns the left or top offset of the screen.
|
|
*/
|
|
static int LToffset(int lort) {
|
|
int Loffset, Toffset;
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
return (lort == SCREENXPOS) ? Loffset : Toffset;
|
|
}
|
|
|
|
/**
|
|
* Set new cursor position.
|
|
*/
|
|
static void MoveCursor(int x, int y) {
|
|
_vm->_cursor->SetCursorXY(x, y);
|
|
|
|
g_controlX = x; // Save these values so that
|
|
g_controlY = y; // control(on) doesn't undo this
|
|
}
|
|
|
|
/**
|
|
* MoveTag(tag, x, y)
|
|
*/
|
|
void MoveTag(int tag, int x, int y, HPOLYGON hp) {
|
|
// Tag could be zero, meaning calling tag
|
|
MovePolygon(TAG, tag ? tag : GetTagPolyId(hp), x, y);
|
|
}
|
|
|
|
/**
|
|
* MoveTagTo(tag, x, y)
|
|
*/
|
|
void MoveTagTo(int tag, int x, int y, HPOLYGON hp) {
|
|
// Tag could be zero, meaning calling tag
|
|
MovePolygonTo(TAG, tag ? tag : GetTagPolyId(hp), x, y);
|
|
}
|
|
|
|
/**
|
|
* Triggers change to a new scene.
|
|
*/
|
|
void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (_vm->_bmv->MoviePlaying()) {
|
|
_vm->_bmv->AbortMovie();
|
|
CORO_SLEEP(2);
|
|
}
|
|
}
|
|
|
|
SetNewScene(scene, entrance, transition);
|
|
|
|
// Prevent tags and cursor re-appearing
|
|
if (TinselVersion >= 2)
|
|
ControlStartOff();
|
|
else
|
|
GetControl(CONTROL_STARTOFF);
|
|
|
|
if (TinselVersion == 1)
|
|
++g_sceneCtr;
|
|
|
|
// Prevent code subsequent to this call running before scene changes
|
|
if (CoroScheduler.getCurrentPID() != PID_MASTER_SCR)
|
|
CORO_KILL_SELF();
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Disable dynamic blocking for current scene.
|
|
*/
|
|
static void NoBlocking() {
|
|
SetNoBlocking(true);
|
|
}
|
|
|
|
/**
|
|
* Define a no-scroll boundary for the current scene.
|
|
*/
|
|
static void NoScroll(int x1, int y1, int x2, int y2) {
|
|
_vm->_scroll->SetNoScroll(x1, y1, x2, y2);
|
|
}
|
|
|
|
/**
|
|
* Hold the specified object.
|
|
*/
|
|
static void ObjectHeld(int object) {
|
|
_vm->_dialogs->holdItem(object);
|
|
}
|
|
|
|
/**
|
|
* Set the top left offset of the screen.
|
|
*/
|
|
void Offset(EXTREME extreme, int x, int y) {
|
|
_vm->_scroll->KillScroll();
|
|
|
|
if (TinselVersion >= 2)
|
|
DecodeExtreme(extreme, &x, &y);
|
|
|
|
_vm->_bg->PlayfieldSetPos(FIELD_WORLD, x, y);
|
|
}
|
|
|
|
/**
|
|
* OtherObject()
|
|
*/
|
|
int OtherObject(const InventoryObject *pinvo) {
|
|
assert(pinvo != NULL);
|
|
|
|
// return held object or object clicked on - whichever is not the calling object
|
|
|
|
// pinvo->id is the calling object
|
|
// whichItemHeld() gives the held object
|
|
// getIcon() gives the object clicked on
|
|
|
|
assert(_vm->_dialogs->getIcon() == pinvo->getId() || _vm->_dialogs->whichItemHeld() == pinvo->getId());
|
|
|
|
if (_vm->_dialogs->getIcon() == pinvo->getId())
|
|
return _vm->_dialogs->whichItemHeld();
|
|
else
|
|
return _vm->_dialogs->getIcon();
|
|
}
|
|
|
|
/**
|
|
* Play a film.
|
|
*/
|
|
static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int compit, int actorid, bool splay, int sfact,
|
|
bool escOn, int myEscape, bool bTop) {
|
|
assert(hFilm != 0); // play(): Trying to play NULL film
|
|
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
|
|
// Don't do CDPlay() for now if already escaped
|
|
if (g_bEscapedCdPlay) {
|
|
g_bEscapedCdPlay = false;
|
|
return;
|
|
}
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
// If this actor is dead, call a stop to the calling process
|
|
if (actorid && !_vm->_actor->actorAlive(actorid))
|
|
CORO_KILL_SELF();
|
|
|
|
// 7/4/95
|
|
if (!escOn)
|
|
myEscape = GetEscEvents();
|
|
|
|
if (compit == 1) {
|
|
// Play to completion before returning
|
|
CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop, nullptr));
|
|
} else if (compit == 2) {
|
|
error("play(): compit == 2 - please advise John");
|
|
} else {
|
|
// Kick off the play and return.
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop, nullptr));
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Play a film
|
|
*/
|
|
static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int compit, int myEscape, bool bTop, TINSEL_EVENT event, HPOLYGON hPoly, int taggedActor) {
|
|
OBJECT** playfield = nullptr;
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
assert(hFilm != 0);
|
|
|
|
// Don't do CdPlay() for now if already escaped
|
|
if (g_bEscapedCdPlay) {
|
|
g_bEscapedCdPlay = false;
|
|
return;
|
|
}
|
|
|
|
if (TinselVersion == 3) {
|
|
CORO_INVOKE_0(_vm->_bg->WaitForBG);
|
|
}
|
|
|
|
if (event == TALKING) {
|
|
int actor;
|
|
if (hPoly == NOPOLY) {
|
|
// Must be a tagged actor
|
|
|
|
assert(taggedActor && _vm->_actor->IsTaggedActor(taggedActor));
|
|
actor = taggedActor;
|
|
} else if (taggedActor == 0) {
|
|
// Must be a polygon with an actor ID
|
|
actor = GetTagPolyId(hPoly);
|
|
assert(actor & ACTORTAG_KEY);
|
|
actor &= ~ACTORTAG_KEY;
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
|
|
_vm->_actor->SetActorTalking(actor, true);
|
|
_vm->_actor->SetActorTalkFilm(actor, hFilm);
|
|
}
|
|
|
|
bool bComplete;
|
|
|
|
bComplete = compit;
|
|
|
|
if (TinselVersion == 3) {
|
|
bComplete = compit & 0x20;
|
|
if (bTop) {
|
|
playfield = _vm->_bg->GetPlayfieldList(FIELD_STATUS);
|
|
} else {
|
|
playfield = _vm->_bg->GetPlayfieldList(compit & 0x0F);
|
|
}
|
|
}
|
|
|
|
if (bComplete) {
|
|
// Play to completion before returning
|
|
CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, 0, false, false, myEscape != 0, myEscape, bTop, playfield));
|
|
} else {
|
|
// Kick off the play and return.
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, myEscape, bTop, playfield));
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Play a midi file.
|
|
*/
|
|
static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
assert(loop == MIDI_DEF || loop == MIDI_LOOP);
|
|
|
|
_vm->_music->PlayMidiSequence(hMidi, loop == MIDI_LOOP);
|
|
|
|
// This check&sleep was added in DW v2. It was most likely added to
|
|
// ensure that the MIDI song started playing before the next opcode
|
|
// is executed.
|
|
// In DW1, it messes up the script arguments when entering the secret
|
|
// door in the bookshelf in the library, leading to a crash, when the
|
|
// music volume is set to 0.
|
|
if (!_vm->_music->MidiPlaying() && TinselVersion >= 2)
|
|
CORO_SLEEP(1);
|
|
|
|
if (complete) {
|
|
while (_vm->_music->MidiPlaying())
|
|
CORO_SLEEP(1);
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Plays a movie
|
|
*/
|
|
|
|
static void PlayMovie(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int i;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (myEscape && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
// Get rid of the cursor
|
|
for (_ctx->i = 0; _ctx->i < 3; _ctx->i++) {
|
|
_vm->_cursor->DwHideCursor();
|
|
_vm->_cursor->DropCursor();
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
// They claim to be getting "Can't play two movies at once!" error
|
|
while (_vm->_bmv->MoviePlaying())
|
|
CORO_SLEEP(1);
|
|
|
|
// Play the movie
|
|
CORO_INVOKE_2(_vm->_bmv->PlayBMV, hFileStem, myEscape);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Plays a movie
|
|
*/
|
|
static void t3PlayMovie(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int i;
|
|
bool hadControl;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (myEscape && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
_ctx->hadControl = GetControl();
|
|
|
|
while (_vm->_bmv->MoviePlaying()) {
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
// Play the movie
|
|
CORO_INVOKE_2(_vm->_bmv->PlayBMV, hFileStem, myEscape);
|
|
|
|
if (_ctx->hadControl) {
|
|
ControlOn();
|
|
}
|
|
|
|
// Change scene
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
/**
|
|
* Play some music
|
|
*/
|
|
static void PlayMusic(int tune) {
|
|
_vm->_pcmMusic->startPlay(tune);
|
|
}
|
|
|
|
static void FadeMusic(int tune, int fadeParams) {
|
|
warning("TODO: Implement fading: %08x", fadeParams);
|
|
_vm->_pcmMusic->startPlay(tune);
|
|
}
|
|
|
|
/**
|
|
* Play a sample.
|
|
* Tinsel 1 version
|
|
*/
|
|
static void PlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
Audio::SoundHandle handle;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
// Don't play SFX if voice is already playing
|
|
if (_vm->_mixer->hasActiveChannelOfType(Audio::Mixer::kSpeechSoundType))
|
|
return;
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
_vm->_sound->stopAllSamples(); // Stop any currently playing sample
|
|
return;
|
|
}
|
|
|
|
if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
|
|
_vm->_sound->playSample(sample, Audio::Mixer::kSFXSoundType, &_ctx->handle);
|
|
|
|
if (bComplete) {
|
|
while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
// Abort if escapable and ESCAPE is pressed
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
break;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
}
|
|
} else {
|
|
// Prevent Glitter lock-up
|
|
CORO_SLEEP(1);
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Play a sample
|
|
* Tinsel 2 version
|
|
*/
|
|
static void PlaySample(CORO_PARAM, int sample, int x, int y, int flags, int myEscape) {
|
|
int priority;
|
|
CORO_BEGIN_CONTEXT;
|
|
Audio::SoundHandle handle;
|
|
int myEscape;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->myEscape = myEscape;
|
|
|
|
// Not escapable if PlaySample(..., s)
|
|
if (flags & PS_SUSTAIN) {
|
|
_ctx->myEscape = 0;
|
|
priority = PRIORITY_SPLAY2;
|
|
} else {
|
|
priority = PRIORITY_SPLAY1;
|
|
}
|
|
|
|
// Don't do anything if it's already been escaped
|
|
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
|
|
if (x == 0)
|
|
x = -1;
|
|
|
|
_vm->_sound->playSample(sample, 0, false, x, y, priority, Audio::Mixer::kSFXSoundType,
|
|
&_ctx->handle);
|
|
|
|
if (flags & PS_COMPLETE) {
|
|
while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
// Abort if escapable and ESCAPE is pressed
|
|
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents()) {
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
break;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
}
|
|
} else {
|
|
// Prevent Glitter lock-up
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Move the cursor to the tagged actor's tag point.
|
|
*/
|
|
void PointActor(int actor) {
|
|
int x, y;
|
|
|
|
// Only do this if the function is enabled
|
|
if (!SysVar(SV_ENABLEPOINTTAG))
|
|
return;
|
|
|
|
assert(_vm->_actor->IsTaggedActor(actor));
|
|
|
|
_vm->_actor->GetActorTagPos(actor, &x, &y, true);
|
|
|
|
_vm->setMousePosition(Common::Point(x, y));
|
|
}
|
|
|
|
/**
|
|
* Move the cursor to the tag's tag point.
|
|
*/
|
|
static void PointTag(int tagno, HPOLYGON hp) {
|
|
int x, y;
|
|
SCNHANDLE junk;
|
|
|
|
// Only do this if the function is enabled
|
|
if (!SysVar(SV_ENABLEPOINTTAG))
|
|
return;
|
|
|
|
// Tag could be zero, meaning calling tag
|
|
if (tagno == 0)
|
|
tagno = GetTagPolyId(hp);
|
|
|
|
GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
|
|
|
|
_vm->setMousePosition(Common::Point(x, y));
|
|
}
|
|
|
|
/**
|
|
* PostActor("actor", event)
|
|
*/
|
|
static void PostActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp,
|
|
int taggedActor, int myEscape) {
|
|
if (actor == -1) {
|
|
actor = taggedActor;
|
|
assert(hp == NOPOLY && taggedActor);
|
|
assert(_vm->_actor->IsTaggedActor(actor));
|
|
}
|
|
|
|
if (_vm->_actor->IsTaggedActor(actor)) {
|
|
assert(actor);
|
|
ActorEvent(coroParam, actor, event, false, myEscape);
|
|
} else {
|
|
PostTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PostGlobalProcess(process#, event)
|
|
*/
|
|
static void PostGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
|
|
GlobalProcessEvent(coroParam, procId, event, false, myEscape);
|
|
}
|
|
|
|
/**
|
|
* PostObject(object, event)
|
|
*/
|
|
static void PostObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
|
|
ObjectEvent(coroParam, object, event, false, myEscape);
|
|
}
|
|
|
|
/**
|
|
* PostProcess(process#, event)
|
|
*/
|
|
static void PostProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
|
|
SceneProcessEvent(coroParam, procId, event, false, myEscape);
|
|
}
|
|
|
|
/**
|
|
* Posts an event to a specified tag
|
|
*/
|
|
static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
|
|
// Tag could be zero, meaning calling tag
|
|
if (tagno == 0) {
|
|
assert(hp != NOPOLY);
|
|
PolygonEvent(coroParam, hp, event, 0, false, myEscape);
|
|
} else {
|
|
assert(IsTagPolygon(tagno));
|
|
PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, false, myEscape);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger pre-loading of a scene's data.
|
|
*/
|
|
static void PrepareScene(SCNHANDLE scene) {
|
|
#ifdef BODGE
|
|
if (!_vm->_handle->ValidHandle(scene))
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Print the given text at the given place for the given time.
|
|
*/
|
|
static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSustain, bool escOn, int myEscape) {
|
|
if (TinselVersion >= 2)
|
|
escOn = myEscape != 0;
|
|
|
|
CORO_BEGIN_CONTEXT;
|
|
OBJECT *pText; // text object pointer
|
|
int myleftEvent;
|
|
bool bSample; // Set if a sample is playing
|
|
Audio::SoundHandle handle;
|
|
int timeout;
|
|
int time;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
bool bJapDoPrintText; // Bodge to get-around Japanese bodge
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->pText = nullptr;
|
|
_ctx->bSample = false;
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (TinselVersion <= 1) {
|
|
// Kick off the voice sample
|
|
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
|
|
_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
|
|
_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
|
|
}
|
|
}
|
|
|
|
// Get the string
|
|
LoadStringRes(text, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
|
|
// Calculate display time
|
|
bJapDoPrintText = false;
|
|
if (time == 0) {
|
|
// This is a 'talky' print
|
|
_ctx->time = TextTime(_vm->_font->TextBufferAddr());
|
|
|
|
// Cut short-able if sustain was not set
|
|
_ctx->myleftEvent = bSustain ? 0 : GetLeftEvents();
|
|
} else {
|
|
_ctx->time = time * ONE_SECOND;
|
|
_ctx->myleftEvent = ((TinselVersion >= 2) && !bSustain) ? GetLeftEvents() : 0;
|
|
if (_vm->_config->isJapanMode())
|
|
bJapDoPrintText = true;
|
|
}
|
|
|
|
// Print the text
|
|
if (TinselVersion >= 2) {
|
|
int Loffset, Toffset;
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
_ctx->pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS),
|
|
_vm->_font->TextBufferAddr(), 0, x - Loffset, y - Toffset, _vm->_font->GetTagFontHandle(),
|
|
TXT_CENTER, 0);
|
|
assert(_ctx->pText);
|
|
|
|
// Adjust x, y, or z if necessary
|
|
KeepOnScreen(_ctx->pText, &x, &y);
|
|
if (_vm->_dialogs->isTopWindow())
|
|
MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
|
|
|
|
} else if (bJapDoPrintText || (!_vm->_config->isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
|
|
int Loffset, Toffset; // Screen position
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
|
|
SCNHANDLE fontHandle = (TinselVersion >= 2) ? _vm->_font->GetTagFontHandle() : _vm->_font->GetTalkFontHandle();
|
|
_ctx->pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
|
0, x - Loffset, y - Toffset,
|
|
fontHandle, TXT_CENTER);
|
|
assert(_ctx->pText); // string produced NULL text
|
|
if (_vm->_dialogs->isTopWindow())
|
|
MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
|
|
|
|
/*
|
|
* New feature: Don't go off the side of the background
|
|
*/
|
|
int shift;
|
|
shift = MultiRightmost(_ctx->pText) + 2;
|
|
if (shift >= _vm->_bg->BgWidth()) // Not off right
|
|
MultiMoveRelXY(_ctx->pText, _vm->_bg->BgWidth() - shift, 0);
|
|
shift = MultiLeftmost(_ctx->pText) - 1;
|
|
if (shift <= 0) // Not off left
|
|
MultiMoveRelXY(_ctx->pText, -shift, 0);
|
|
shift = MultiLowest(_ctx->pText);
|
|
if (shift > _vm->_bg->BgHeight()) // Not off bottom
|
|
MultiMoveRelXY(_ctx->pText, 0, _vm->_bg->BgHeight() - shift);
|
|
}
|
|
|
|
// Give up if nothing printed and no sample
|
|
if (_ctx->pText == NULL && !_ctx->bSample)
|
|
return;
|
|
|
|
// Leave it up until time runs out or whatever
|
|
if (TinselVersion >= 2) {
|
|
do {
|
|
CORO_SLEEP(1);
|
|
|
|
// Cancelled?
|
|
if ( (myEscape && myEscape != GetEscEvents())
|
|
|| (!bSustain && LeftEventChange(_ctx->myleftEvent)))
|
|
break;
|
|
|
|
} while (_ctx->time-- >= 0);
|
|
|
|
} else {
|
|
_ctx->timeout = SAMPLETIMEOUT;
|
|
do {
|
|
CORO_SLEEP(1);
|
|
|
|
// Abort if escapable and ESCAPE is pressed
|
|
// Abort if left click - hardwired feature for talky-print!
|
|
// Will be ignored if myleftevent happens to be 0!
|
|
// Abort if sample times out
|
|
if ((escOn && myEscape != GetEscEvents())
|
|
|| (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents())
|
|
|| (_ctx->bSample && --_ctx->timeout <= 0))
|
|
break;
|
|
|
|
if (_ctx->bSample) {
|
|
// Wait for sample to end whether or not
|
|
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
|
|
// No text or speed modification - just depends on sample
|
|
break;
|
|
} else {
|
|
// Must wait for time
|
|
_ctx->bSample = false;
|
|
}
|
|
}
|
|
} else {
|
|
// No sample - just depends on time
|
|
if (_ctx->time-- <= 0)
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
}
|
|
|
|
// Delete the text
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const InventoryObject *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
|
|
static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
|
|
|
|
/**
|
|
* Print the given inventory object's name or whatever.
|
|
*/
|
|
static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const InventoryObject *pinvo, const int event, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
OBJECT *pText; // text object pointer
|
|
int textx, texty;
|
|
int item;
|
|
bool bSample;
|
|
int sub;
|
|
Audio::SoundHandle handle;
|
|
int ticks;
|
|
int timeout;
|
|
bool bTookControl;
|
|
int myEscape;
|
|
int myLeftEvent;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
assert(pinvo != 0); // PrintObj() may only be called from an object code block
|
|
_ctx->myEscape = myEscape;
|
|
|
|
if (hText == (SCNHANDLE)-1) { // 'OFF'
|
|
g_bNotPointedRunning = true;
|
|
return;
|
|
}
|
|
if (hText == (SCNHANDLE)-2) { // 'ON'
|
|
g_bNotPointedRunning = false;
|
|
return;
|
|
}
|
|
|
|
// Don't do it if it's not wanted
|
|
if ((TinselVersion >= 2) && (myEscape) && (myEscape != GetEscEvents()))
|
|
return;
|
|
|
|
/*
|
|
* Find out which icon the cursor is over, and where to put the text.
|
|
*/
|
|
_vm->_cursor->GetCursorXY(&_ctx->textx, &_ctx->texty, false); // Cursor position..
|
|
_ctx->item = _vm->_dialogs->invItem(&_ctx->textx, &_ctx->texty, true); // ..to text position
|
|
if (_ctx->item == INV_NOICON)
|
|
return;
|
|
|
|
/*
|
|
* POINT/other event PrintObj() arbitration...
|
|
*/
|
|
if (event != POINTED) {
|
|
g_bNotPointedRunning = true; // Get POINTED text to die
|
|
CORO_SLEEP(1); // Give it chance to
|
|
} else if (TinselVersion <= 1)
|
|
g_bNotPointedRunning = false; // There may have been an OFF without an ON
|
|
|
|
// Make multi-ones escape
|
|
if ((TinselVersion >= 2) && (SubStringCount(hText) > 1) && !_ctx->myEscape)
|
|
_ctx->myEscape = GetEscEvents();
|
|
|
|
// Loop once for Tinsel 1 strings, and for Tinsel 2 however many lines are needed
|
|
for (_ctx->sub = 0; _ctx->sub < ((TinselVersion >= 2) ? SubStringCount(hText) : 1); _ctx->sub++) {
|
|
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
|
|
break;
|
|
|
|
if (!_vm->_sound->sampleExists(hText))
|
|
_ctx->bSample = false;
|
|
else {
|
|
// Kick off the voice sample
|
|
_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK,
|
|
Audio::Mixer::kSpeechSoundType, &_ctx->handle);
|
|
_ctx->bSample = true;
|
|
}
|
|
|
|
// Display the text and set it's Z position
|
|
if (event == POINTED || (!_vm->_config->isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
|
|
int xshift;
|
|
|
|
// Get the text string
|
|
if (TinselVersion >= 2)
|
|
LoadSubString(hText, _ctx->sub, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
else
|
|
LoadStringRes(hText, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
|
|
_ctx->pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
|
0, _ctx->textx, _ctx->texty, _vm->_font->GetTagFontHandle(), TXT_CENTER);
|
|
assert(_ctx->pText); // PrintObj() string produced NULL text
|
|
|
|
MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
|
|
|
|
if (TinselVersion >= 2)
|
|
KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
|
|
else {
|
|
// Don't go off the side of the screen
|
|
xshift = MultiLeftmost(_ctx->pText);
|
|
if (xshift < 0) {
|
|
MultiMoveRelXY(_ctx->pText, - xshift, 0);
|
|
_ctx->textx -= xshift;
|
|
}
|
|
xshift = MultiRightmost(_ctx->pText);
|
|
if (xshift > SCREEN_WIDTH) {
|
|
MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
|
|
_ctx->textx += SCREEN_WIDTH - xshift;
|
|
}
|
|
}
|
|
} else
|
|
_ctx->pText = nullptr;
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (event == POINTED) {
|
|
/*
|
|
* PrintObj() called from POINT event
|
|
*/
|
|
// Have to give way to non-POINTED-generated text
|
|
// and go away if the item gets picked up
|
|
int x, y;
|
|
do {
|
|
// Give up if this item gets picked up
|
|
if (_vm->_dialogs->whichItemHeld() == pinvo->getId())
|
|
break;
|
|
|
|
// Give way to non-POINTED-generated text
|
|
if (g_bNotPointedRunning) {
|
|
// Delete the text, and wait for the all-clear
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
|
|
|
while (g_bNotPointedRunning)
|
|
CORO_SLEEP(1);
|
|
|
|
_vm->_cursor->GetCursorXY(&x, &y, false);
|
|
if (_vm->_dialogs->invItem(&x, &y, false) != _ctx->item)
|
|
break;
|
|
|
|
// Re-display in the same place
|
|
LoadStringRes(hText, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
_ctx->pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS),
|
|
_vm->_font->TextBufferAddr(), 0, _ctx->textx, _ctx->texty, _vm->_font->GetTagFontHandle(),
|
|
TXT_CENTER, 0);
|
|
assert(_ctx->pText);
|
|
|
|
KeepOnScreen(_ctx->pText, &_ctx->textx, &_ctx->texty);
|
|
MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Carry on until the cursor leaves this icon
|
|
_vm->_cursor->GetCursorXY(&x, &y, false);
|
|
|
|
} while (_vm->_dialogs->invItemId(x, y) == pinvo->getId());
|
|
} else {
|
|
/*
|
|
* PrintObj() called from other event
|
|
*/
|
|
_ctx->myLeftEvent = GetLeftEvents();
|
|
_ctx->bTookControl = GetControl();
|
|
|
|
// Display for a time, but abort if conversation gets hidden
|
|
if (_ctx->pText)
|
|
_ctx->ticks = TextTime(_vm->_font->TextBufferAddr());
|
|
_ctx->timeout = SAMPLETIMEOUT;
|
|
|
|
for (;;) {
|
|
CORO_SLEEP(1);
|
|
|
|
// Abort if left click - hardwired feature for talky-print!
|
|
// Abort if sample times out
|
|
// Abort if conversation hidden
|
|
if (LeftEventChange(_ctx->myLeftEvent)
|
|
|| --_ctx->timeout <= 0 || _vm->_dialogs->convIsHidden())
|
|
break;
|
|
|
|
if (_ctx->bSample) {
|
|
// Wait for sample to end whether or not
|
|
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
|
|
// No text or speed modification - just depends on sample
|
|
break;
|
|
} else {
|
|
// Must wait for time
|
|
_ctx->bSample = false;
|
|
}
|
|
}
|
|
|
|
// Decrement the subtitles timeout counter
|
|
if (_ctx->ticks > 0) --_ctx->ticks;
|
|
|
|
} else {
|
|
// No sample - just depends on time
|
|
if (_ctx->ticks-- <= 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (_ctx->bTookControl)
|
|
ControlOn(); // Free control if we took it
|
|
}
|
|
|
|
} else {
|
|
if (event == POINTED) {
|
|
// FIXME: Is there ever an associated sound if in POINTED mode???
|
|
assert(!_vm->_sound->sampleExists(hText));
|
|
CORO_INVOKE_ARGS(PrintObjPointed, (CORO_SUBCTX, hText, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item));
|
|
} else {
|
|
CORO_INVOKE_2(PrintObjNonPointed, hText, _ctx->pText);
|
|
}
|
|
}
|
|
|
|
// Delete the text, if haven't already
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
|
|
|
// If it hasn't already finished, stop sample
|
|
if (_ctx->bSample)
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
}
|
|
|
|
// Let POINTED text back in if this is the last
|
|
if (event != POINTED)
|
|
g_bNotPointedRunning = false;
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const InventoryObject *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
// Have to give way to non-POINTED-generated text
|
|
// and go away if the item gets picked up
|
|
int x, y;
|
|
do {
|
|
// Give up if this item gets picked up
|
|
if (_vm->_dialogs->whichItemHeld() == pinvo->getId())
|
|
break;
|
|
|
|
// Give way to non-POINTED-generated text
|
|
if (g_bNotPointedRunning) {
|
|
// Delete the text, and wait for the all-clear
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &pText);
|
|
while (g_bNotPointedRunning)
|
|
CORO_SLEEP(1);
|
|
|
|
_vm->_cursor->GetCursorXY(&x, &y, false);
|
|
if (_vm->_dialogs->invItem(&x, &y, false) != item)
|
|
break;
|
|
|
|
// Re-display in the same place
|
|
LoadStringRes(text, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS), _vm->_font->TextBufferAddr(),
|
|
0, textx, texty, _vm->_font->GetTagFontHandle(), TXT_CENTER);
|
|
assert(pText); // PrintObj() string produced NULL text
|
|
MultiSetZPosition(pText, Z_INV_ITEXT);
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Carry on until the cursor leaves this icon
|
|
_vm->_cursor->GetCursorXY(&x, &y, false);
|
|
} while (_vm->_dialogs->invItemId(x, y) == pinvo->getId());
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) {
|
|
CORO_BEGIN_CONTEXT;
|
|
bool bSample; // Set if a sample is playing
|
|
Audio::SoundHandle handle;
|
|
|
|
int myleftEvent;
|
|
bool took_control;
|
|
int ticks;
|
|
int timeout;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
// Kick off the voice sample
|
|
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
|
|
_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
|
|
_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
|
|
} else
|
|
_ctx->bSample = false;
|
|
|
|
_ctx->myleftEvent = GetLeftEvents();
|
|
_ctx->took_control = GetControl(CONTROL_OFF);
|
|
|
|
// Display for a time, but abort if conversation gets hidden
|
|
if (_vm->_config->isJapanMode())
|
|
_ctx->ticks = JAP_TEXT_TIME;
|
|
else if (pText)
|
|
_ctx->ticks = TextTime(_vm->_font->TextBufferAddr());
|
|
else
|
|
_ctx->ticks = 0;
|
|
|
|
_ctx->timeout = SAMPLETIMEOUT;
|
|
do {
|
|
CORO_SLEEP(1);
|
|
--_ctx->timeout;
|
|
|
|
// Abort if left click - hardwired feature for talky-print!
|
|
// Abort if sample times out
|
|
// Abort if conversation hidden
|
|
if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || _vm->_dialogs->convIsHidden())
|
|
break;
|
|
|
|
if (_ctx->bSample) {
|
|
// Wait for sample to end whether or not
|
|
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
if (pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
|
|
// No text or speed modification - just depends on sample
|
|
break;
|
|
} else {
|
|
// Must wait for time
|
|
_ctx->bSample = false;
|
|
}
|
|
}
|
|
|
|
// Decrement the subtitles timeout counter
|
|
if (_ctx->ticks > 0) --_ctx->ticks;
|
|
|
|
} else {
|
|
// No sample - just depends on time
|
|
if (_ctx->ticks-- <= 0)
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
g_bNotPointedRunning = false; // Let POINTED text back in
|
|
|
|
if (_ctx->took_control)
|
|
Control(CONTROL_ON); // Free control if we took it
|
|
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Register the fact that this poly would like its tag displayed.
|
|
*/
|
|
static void PrintTag(HPOLYGON hp, SCNHANDLE text, int actor = 0, bool bCursor = false) {
|
|
// printtag() may only be called from a polygon code block in Tinsel 1, or
|
|
// additionally from a moving actor code block in Tinsel 2
|
|
assert((hp != NOPOLY) || ((TinselVersion >= 2) && (actor != 0)));
|
|
|
|
if (hp != NOPOLY) {
|
|
// Poly handling
|
|
if (TinselVersion >= 2)
|
|
SetPolyTagWanted(hp, true, bCursor, text);
|
|
else if (PolyTagState(hp) == TAG_OFF) {
|
|
SetPolyTagState(hp, TAG_ON);
|
|
SetPolyTagHandle(hp, text);
|
|
}
|
|
} else {
|
|
// Moving actor handling
|
|
_vm->_actor->SetActorTagWanted(actor, true, bCursor, text);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Quits the game
|
|
*/
|
|
static void QuitGame() {
|
|
_vm->_music->StopMidi();
|
|
StopSample();
|
|
_vm->quitGame();
|
|
}
|
|
|
|
/**
|
|
* Return a random number between optional limits.
|
|
*/
|
|
static int RandomFn(int n1, int n2, int norpt) {
|
|
int i = 0;
|
|
uint32 value;
|
|
|
|
// In DW1 demo, upper/lower limit can be reversed
|
|
if (n2 < n1) SWAP(n1, n2);
|
|
|
|
do {
|
|
value = n1 + _vm->getRandomNumber(n2 - n1);
|
|
} while ((g_lastValue == value) && (norpt == RAND_NORPT) && (++i <= 10));
|
|
|
|
g_lastValue = value;
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* ResetIdleTime
|
|
*/
|
|
void ResetIdleTime() {
|
|
resetUserEventTime();
|
|
}
|
|
|
|
/**
|
|
* FnRestartGame
|
|
*/
|
|
void FnRestartGame() {
|
|
// TODO: Tinsel 2 comments out the 2 calls, but I'm not sure that this should be done
|
|
_vm->_music->StopMidi();
|
|
StopSample();
|
|
|
|
g_bRestart = true;
|
|
g_sceneCtr = 0;
|
|
}
|
|
|
|
/**
|
|
* Restore saved scene.
|
|
*/
|
|
static void RestoreScene(CORO_PARAM, TRANSITS transition) {
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (_vm->_bmv->MoviePlaying()) {
|
|
_vm->_bmv->AbortMovie();
|
|
CORO_SLEEP(2);
|
|
}
|
|
|
|
CuttingScene(false);
|
|
|
|
} else {
|
|
UnSuspendHook();
|
|
}
|
|
|
|
TinselRestoreScene(transition == TRANS_FADE);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Resumes the last game
|
|
*/
|
|
void ResumeLastGame() {
|
|
RestoreGame(NewestSavedGame());
|
|
}
|
|
|
|
/**
|
|
* Returns the current run mode
|
|
*/
|
|
static int RunMode() {
|
|
return 0; //clRunMode;
|
|
}
|
|
|
|
/**
|
|
* SamplePlaying
|
|
*/
|
|
static bool SamplePlaying(bool escOn, int myEscape) {
|
|
// escape effects introduced 14/12/95 to fix
|
|
// while (sampleplaying()) pause;
|
|
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return false;
|
|
|
|
return _vm->_sound->sampleIsPlaying();
|
|
}
|
|
|
|
/**
|
|
* Save current scene.
|
|
*/
|
|
void SaveScene(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (TinselVersion >= 2) {
|
|
CuttingScene(true);
|
|
SendSceneTinselProcess(LEAVE_T2);
|
|
CORO_GIVE_WAY;
|
|
|
|
CORO_INVOKE_0(TinselSaveScene);
|
|
} else {
|
|
CORO_INVOKE_0(TinselSaveScene);
|
|
SuspendHook();
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* ScalingReels
|
|
*/
|
|
static void ScalingReels(int actor, int scale, int direction,
|
|
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
|
|
|
|
SetScalingReels(actor, scale, direction, left, right, forward, away);
|
|
}
|
|
|
|
/**
|
|
* Return the icon that caused the CONVERSE event.
|
|
*/
|
|
static int ScanIcon() {
|
|
return _vm->_dialogs->getIcon();
|
|
}
|
|
|
|
/**
|
|
* Scroll the screen to target co-ordinates.
|
|
*/
|
|
static void ScrollScreen(CORO_PARAM, EXTREME extreme, int xp, int yp, int xIter, int yIter, bool bComp, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int thisScroll;
|
|
int x, y;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
++g_scrollNumber;
|
|
_ctx->x = xp;
|
|
_ctx->y = yp;
|
|
|
|
if (((TinselVersion >= 2) && g_bInstantScroll) || (escOn && myEscape != GetEscEvents())) {
|
|
// Instant completion!
|
|
Offset(extreme, _ctx->x, _ctx->y);
|
|
} else {
|
|
_ctx->thisScroll = g_scrollNumber;
|
|
if (TinselVersion >= 2)
|
|
DecodeExtreme(extreme, &_ctx->x, &_ctx->y);
|
|
|
|
_vm->_scroll->ScrollTo(_ctx->x, _ctx->y, xIter, yIter);
|
|
|
|
if (bComp) {
|
|
int Loffset, Toffset;
|
|
do {
|
|
CORO_SLEEP(1);
|
|
|
|
// If escapable and ESCAPE is pressed...
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
// Instant completion!
|
|
Offset(extreme, _ctx->x, _ctx->y);
|
|
break;
|
|
}
|
|
|
|
// give up if have been superseded
|
|
if (_ctx->thisScroll != g_scrollNumber)
|
|
CORO_KILL_SELF();
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
} while (Loffset != _ctx->x || Toffset != _ctx->y);
|
|
} else if ((TinselVersion >= 2) && myEscape) {
|
|
SCROLL_MONITOR sm;
|
|
|
|
// Scroll is escapable even though we're not waiting for it
|
|
sm.x = _ctx->x;
|
|
sm.y = _ctx->y;
|
|
sm.thisScroll = g_scrollNumber;
|
|
sm.myEscape = myEscape;
|
|
CoroScheduler.createProcess(PID_TCODE, ScrollMonitorProcess, &sm, sizeof(sm));
|
|
}
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* ScrollParameters
|
|
*/
|
|
static void ScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
|
|
int yTriggerBottom, int yDistance, int ySpeed) {
|
|
_vm->_scroll->SetScrollParameters(xTrigger, xDistance, xSpeed,
|
|
yTriggerTop, yTriggerBottom, yDistance, ySpeed);
|
|
}
|
|
|
|
/**
|
|
* SendActor("actor", event)
|
|
*/
|
|
int SendActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
|
|
bool result;
|
|
|
|
if (_vm->_actor->IsTaggedActor(actor)) {
|
|
assert(actor);
|
|
ActorEvent(coroParam, actor, event, true, myEscape, &result);
|
|
} else {
|
|
SendTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape, &result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* SendGlobalProcess(process#, event)
|
|
*/
|
|
static int SendGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
|
|
return GlobalProcessEvent(coroParam, procId, event, true, myEscape);
|
|
}
|
|
|
|
/**
|
|
* SendObject(object, event)
|
|
*/
|
|
static int SendObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
|
|
bool result;
|
|
ObjectEvent(coroParam, object, event, true, myEscape, &result);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* SendProcess(process#, event)
|
|
*/
|
|
static int SendProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
|
|
bool result;
|
|
SceneProcessEvent(coroParam, procId, event, true, myEscape, &result);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* SendTag(tag#, event)
|
|
*/
|
|
static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result) {
|
|
// Tag could be zero, meaning calling tag
|
|
if (tagno == 0) {
|
|
assert(hp != NOPOLY);
|
|
|
|
PolygonEvent(coroParam, hp, event, 0, true, myEscape, result);
|
|
} else {
|
|
assert(IsTagPolygon(tagno));
|
|
|
|
PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, true, myEscape, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Un-kill an actor.
|
|
*/
|
|
static void SetActor(int actor) {
|
|
_vm->_actor->EnableActor(actor);
|
|
}
|
|
|
|
/**
|
|
* Turn a blocking polygon on.
|
|
*/
|
|
|
|
static void SetBlock(int blockno) {
|
|
EnableBlock(blockno);
|
|
}
|
|
|
|
/**
|
|
* Turn an exit on.
|
|
*/
|
|
|
|
static void SetExit(int exitno) {
|
|
EnableExit(exitno);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void SetInvLimit(int invno, int n) {
|
|
_vm->_dialogs->invSetLimit(invno, n);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void SetInvSize(int invno, int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
|
|
_vm->_dialogs->invSetSize(invno, MinWidth, MinHeight, StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void SetLanguage(LANGUAGE lang) {
|
|
assert(lang == TXT_ENGLISH || lang == TXT_FRENCH
|
|
|| lang == TXT_GERMAN || lang == TXT_ITALIAN
|
|
|| lang == TXT_SPANISH); // ensure language is valid
|
|
|
|
ChangeLanguage(lang);
|
|
}
|
|
|
|
/**
|
|
* Set palette
|
|
*/
|
|
static void SetPalette(SCNHANDLE hPal, bool escOn, int myEscape) {
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
_vm->_bg->ChangePalette(hPal);
|
|
}
|
|
|
|
/**
|
|
* Set system reel
|
|
*/
|
|
static void SetSystemReel(int index, SCNHANDLE reel) {
|
|
_vm->_systemReel->set(index, reel);
|
|
}
|
|
|
|
/**
|
|
* SetSystemString
|
|
*/
|
|
|
|
static void SetSystemString(int stringId, SCNHANDLE hString) {
|
|
SetSysString(stringId, hString);
|
|
}
|
|
|
|
/**
|
|
* Set a system variable
|
|
*/
|
|
static void SetSystemVar(int varId, int newValue) {
|
|
SetSysVar(varId, newValue);
|
|
}
|
|
|
|
/**
|
|
* Turn a tag on.
|
|
*/
|
|
static void SetTag(CORO_PARAM, int tagno) {
|
|
EnableTag(coroParam, tagno);
|
|
}
|
|
|
|
/**
|
|
* Initialize a timer.
|
|
*/
|
|
static void SetTimer(int timerno, int start, bool up, bool frame) {
|
|
StartTimer(timerno, start, up != 0, frame != 0);
|
|
}
|
|
|
|
/**
|
|
* Shell("cmdline")
|
|
*/
|
|
static void Shell(SCNHANDLE commandLine) {
|
|
LoadStringRes(commandLine, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
error("Tried to execute shell command \"%s\"", _vm->_font->TextBufferAddr());
|
|
}
|
|
|
|
/**
|
|
* Don't hide an actors graphics.
|
|
*/
|
|
static void ShowActorFn(CORO_PARAM, int actor) {
|
|
ShowActor(coroParam, actor);
|
|
}
|
|
|
|
/**
|
|
* Turn a blocking polygon on.
|
|
*/
|
|
void ShowBlock(int blockno) {
|
|
EnableBlock(blockno);
|
|
}
|
|
|
|
/**
|
|
* Turn an effect polygon on.
|
|
*/
|
|
void ShowEffect(int effect) {
|
|
EnableEffect(effect);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* Enable display of diagnostic co-ordinates.
|
|
*/
|
|
static void showpos() {
|
|
setshowpos();
|
|
}
|
|
|
|
/**
|
|
* Enable display of diagnostic co-ordinates.
|
|
*/
|
|
static void showstring() {
|
|
setshowstring();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Shows the main menu
|
|
*/
|
|
static void ShowMenu() {
|
|
_vm->_dialogs->openMenu(MAIN_MENU);
|
|
}
|
|
|
|
/**
|
|
* Turn a path on.
|
|
*/
|
|
static void ShowPath(int path) {
|
|
EnablePath(path);
|
|
}
|
|
|
|
/**
|
|
* Turn a refer on.
|
|
*/
|
|
void ShowRefer(int refer) {
|
|
EnableRefer(refer);
|
|
}
|
|
|
|
/**
|
|
* Turn a tag on.
|
|
*/
|
|
static void ShowTag(CORO_PARAM, int tag, HPOLYGON hp) {
|
|
// Tag could be zero, meaning calling tag
|
|
EnableTag(coroParam, tag ? tag : GetTagPolyId(hp));
|
|
}
|
|
|
|
/**
|
|
* Special play - slow down associated actor's movement while the play
|
|
* is running. After the play, position the actor where the play left
|
|
* it and continue walking, if the actor still is.
|
|
*/
|
|
static void SPlay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myEscape) {
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
Play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myEscape, false);
|
|
}
|
|
|
|
/**
|
|
* (Re)Position an actor.
|
|
* If moving actor is not around yet in this scene, start it up.
|
|
*/
|
|
void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm) {
|
|
CORO_BEGIN_CONTEXT;
|
|
MOVER *pMover; // Moving actor structure
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->pMover = GetMover(actor);
|
|
assert((TinselVersion <= 1) || (_ctx->pMover != NULL));
|
|
|
|
if (_ctx->pMover) {
|
|
if (TinselVersion >= 2) {
|
|
// New special. If no paths, just ignore this
|
|
if (PathCount() == 0)
|
|
return;
|
|
|
|
// Another new special.
|
|
// If lead actor, and TalkVia, ignore
|
|
if ((actor == _vm->_actor->GetLeadId() || actor == LEAD_ACTOR) && SysVar(ISV_DIVERT_ACTOR))
|
|
return;
|
|
}
|
|
|
|
if (!MoverIs(_ctx->pMover)) {
|
|
// create a moving actor process
|
|
MoverProcessCreate(x, y, (actor == LEAD_ACTOR) ? _vm->_actor->GetLeadId() : actor, _ctx->pMover);
|
|
|
|
if (hFilm == TF_NONE) {
|
|
// Make sure there is an assigned actorObj
|
|
while (!_ctx->pMover->actorObj)
|
|
CORO_SLEEP(1);
|
|
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else {
|
|
// Check hFilm against certain constants. Note that a switch statement isn't
|
|
// used here because it would interfere with our co-routine implementation
|
|
if (hFilm == TF_UP) {
|
|
if (TinselVersion >= 2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, AWAY);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_DOWN) {
|
|
if (TinselVersion >= 2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, FORWARD);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_LEFT) {
|
|
if (TinselVersion >= 2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, LEFTREEL);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_RIGHT) {
|
|
if (TinselVersion >= 2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, RIGHTREEL);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm != TF_NONE) {
|
|
if (TinselVersion >= 2) CORO_GIVE_WAY;
|
|
AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
|
|
}
|
|
}
|
|
} else {
|
|
switch (hFilm) {
|
|
case TF_NONE:
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
break;
|
|
|
|
case TF_UP:
|
|
SetMoverDirection(_ctx->pMover, AWAY);
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
SetMoverStanding(_ctx->pMover);
|
|
break;
|
|
case TF_DOWN:
|
|
SetMoverDirection(_ctx->pMover, FORWARD);
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
SetMoverStanding(_ctx->pMover);
|
|
break;
|
|
case TF_LEFT:
|
|
SetMoverDirection(_ctx->pMover, LEFTREEL);
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
SetMoverStanding(_ctx->pMover);
|
|
break;
|
|
case TF_RIGHT:
|
|
SetMoverDirection(_ctx->pMover, RIGHTREEL);
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
SetMoverStanding(_ctx->pMover);
|
|
break;
|
|
|
|
default:
|
|
if (x != -1 && y != -1)
|
|
PositionMover(_ctx->pMover, x, y);
|
|
AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
|
|
break;
|
|
}
|
|
}
|
|
} else if (actor == NULL_ACTOR) {
|
|
//
|
|
} else {
|
|
assert(hFilm != 0); // Trying to play NULL film
|
|
|
|
// Kick off the play and return.
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actor, false, 0, false, 0, false, nullptr));
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Position the actor at the polygon's tag node.
|
|
*/
|
|
static void StandTag(int actor, HPOLYGON hp) {
|
|
SCNHANDLE hFilm;
|
|
int pnodex, pnodey;
|
|
|
|
assert(hp != NOPOLY); // StandTag() may only be called from a polygon code block
|
|
|
|
// Where to stand
|
|
GetPolyNode(hp, &pnodex, &pnodey);
|
|
|
|
// Lead actor uses tag node film
|
|
hFilm = GetPolyFilm(hp);
|
|
|
|
// other actors can use direction
|
|
if (TinselVersion >= 2) {
|
|
if (actor != LEAD_ACTOR && actor != _vm->_actor->GetLeadId()
|
|
&& hFilm != TF_UP && hFilm != TF_DOWN
|
|
&& hFilm != TF_LEFT && hFilm != TF_RIGHT)
|
|
hFilm = 0;
|
|
|
|
Stand(Common::nullContext, actor, pnodex, pnodey, hFilm);
|
|
|
|
} else if (hFilm && (actor == LEAD_ACTOR || actor == _vm->_actor->GetLeadId()))
|
|
Stand(Common::nullContext, actor, pnodex, pnodey, hFilm);
|
|
else
|
|
Stand(Common::nullContext, actor, pnodex, pnodey, 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* StartGlobalProcess
|
|
*/
|
|
static void StartGlobalProcess(CORO_PARAM, uint32 procID) {
|
|
GlobalProcessEvent(coroParam, procID, STARTUP, false, 0);
|
|
}
|
|
|
|
/**
|
|
* StartProcess
|
|
*/
|
|
static void StartProcess(CORO_PARAM, uint32 procID) {
|
|
SceneProcessEvent(coroParam, procID, STARTUP, false, 0);
|
|
}
|
|
|
|
/**
|
|
* Initialize a timer.
|
|
*/
|
|
static void StartTimerFn(int timerno, int start, bool up, int fs) {
|
|
StartTimer(timerno, start, up, fs);
|
|
}
|
|
|
|
void StopMidiFn() {
|
|
_vm->_music->StopMidi(); // Stop any currently playing midi
|
|
}
|
|
|
|
/**
|
|
* Kill a specific sample, or all samples.
|
|
*/
|
|
void StopSample(int sample) {
|
|
if (sample == -1)
|
|
_vm->_sound->stopAllSamples(); // Stop any currently playing sample
|
|
else
|
|
_vm->_sound->stopSpecSample(sample, 0);
|
|
}
|
|
|
|
/**
|
|
* Kill a moving actor's walk.
|
|
*/
|
|
static void StopWalk(int actor) {
|
|
MOVER *pMover;
|
|
|
|
pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
|
|
StopMover(pMover); // Cause the actor to stop
|
|
} else {
|
|
GetToken(pMover->actorToken); // Kill the walk process
|
|
pMover->bStop = true; // Cause the actor to stop
|
|
FreeToken(pMover->actorToken);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Subtitles on/off
|
|
*/
|
|
static void Subtitles(int onoff) {
|
|
assert (onoff == ST_ON || onoff == ST_OFF);
|
|
|
|
if (_vm->_config->isJapanMode())
|
|
return; // Subtitles are always off in JAPAN version (?)
|
|
|
|
_vm->_config->_useSubtitles = (onoff == ST_ON);
|
|
}
|
|
|
|
/**
|
|
* Special walk.
|
|
* Walk into or out of a legal path.
|
|
*/
|
|
static void Swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, int32 zOverride, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
bool bTookControl; // Set if this function takes control
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
HPOLYGON hPath;
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
if (TinselVersion >= 2) {
|
|
if (x2 == -1 && y2 == -1)
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
|
|
else
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// For lead actor, lock out the user (if not already locked out)
|
|
if (actor == _vm->_actor->GetLeadId() || actor == LEAD_ACTOR) {
|
|
_ctx->bTookControl = GetControl(CONTROL_OFFV2);
|
|
if ((TinselVersion >= 2) && _ctx->bTookControl)
|
|
_vm->_cursor->RestoreMainCursor();
|
|
} else {
|
|
_ctx->bTookControl = false;
|
|
}
|
|
|
|
if ((TinselVersion >= 2) && (x2 == -1) && (y2 == -1)) {
|
|
// First co-ordinates are the destination
|
|
x2 = x1;
|
|
y2 = y1;
|
|
} else {
|
|
// Stand at the start co-ordinates
|
|
hPath = InPolygon(x1, y1, PATH);
|
|
|
|
if (hPath != NOPOLY) {
|
|
// Walking out of a path
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
|
|
} else {
|
|
hPath = InPolygon(x2, y2, PATH);
|
|
// One of them has to be in a path
|
|
assert(hPath != NOPOLY); //one co-ordinate must be in a legal path
|
|
|
|
// Walking into a path
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0)); // Get path's characteristics
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
|
|
}
|
|
|
|
if ((TinselVersion >= 2) && (zOverride != -1)) {
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
SetMoverZ(pMover, y1, zOverride);
|
|
}
|
|
}
|
|
|
|
CORO_INVOKE_ARGS(Walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, zOverride, escOn, myEscape));
|
|
|
|
// Free control if we took it
|
|
if (_ctx->bTookControl)
|
|
Control(CONTROL_ON);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Gets a system variable
|
|
*/
|
|
static int SystemVar(int varId) {
|
|
return SysVar(varId);
|
|
}
|
|
|
|
/**
|
|
* Define a tagged actor.
|
|
*/
|
|
static void TagActor(int actor, SCNHANDLE text, int tp) {
|
|
_vm->_actor->Tag_Actor(actor, text, tp);
|
|
}
|
|
|
|
/**
|
|
* TagPos([tag #])
|
|
*/
|
|
static int TagPos(MASTER_LIB_CODES operand, int tagno, HPOLYGON hp) {
|
|
int x, y;
|
|
|
|
// Tag could be zero, meaning calling tag
|
|
if (tagno == 0)
|
|
tagno = GetTagPolyId(hp);
|
|
|
|
if (operand == TAGTAGXPOS || operand == TAGTAGYPOS) {
|
|
SCNHANDLE junk;
|
|
|
|
GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
|
|
} else {
|
|
GetPolyNode(GetTagHandle(tagno), &x, &y);
|
|
}
|
|
|
|
if (operand == TAGTAGXPOS || operand == TAGWALKXPOS)
|
|
return x;
|
|
else
|
|
return y;
|
|
}
|
|
|
|
/**
|
|
* Text goes over actor's head while actor plays the talk reel.
|
|
*/
|
|
static void FinishTalkingReel(CORO_PARAM, MOVER *pMover, int actor) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (pMover) {
|
|
SetMoverStanding(pMover);
|
|
AlterMover(pMover, 0, AR_POPREEL);
|
|
} else {
|
|
_vm->_actor->SetActorTalking(actor, false);
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, _vm->_actor->GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false, _vm->_bg->GetPlayfieldList(FIELD_WORLD)));
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x, int y,
|
|
SCNHANDLE hFilm, int actorId, bool bSustain, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int Loffset, Toffset; // Top left of display
|
|
int actor; // The speaking actor
|
|
MOVER *pActor; // For moving actors
|
|
int myLeftEvent;
|
|
int escEvents;
|
|
int ticks;
|
|
bool bTookControl; // Set if this function takes control
|
|
bool bTookTags; // Set if this function disables tags
|
|
OBJECT *pText; // text object pointer
|
|
bool bSample; // Set if a sample is playing
|
|
bool bSamples;
|
|
bool bTalkReel; // Set while talk reel is playing
|
|
Audio::SoundHandle handle;
|
|
int timeout;
|
|
|
|
SPEECH_TYPE whatSort;
|
|
TFTYPE direction;
|
|
int sub;
|
|
int x, y;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->whatSort = speechType;
|
|
_ctx->escEvents = myEscape;
|
|
_ctx->x = x;
|
|
_ctx->y = y;
|
|
_ctx->Loffset = 0;
|
|
_ctx->Toffset = 0;
|
|
_ctx->ticks = 0;
|
|
_ctx->pText = nullptr;
|
|
|
|
// If waiting is enabled, wait for ongoing scroll
|
|
if ((TinselVersion >= 2) && SysVar(SV_SPEECHWAITS))
|
|
CORO_INVOKE_1(WaitScroll, myEscape);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
_ctx->myLeftEvent = GetLeftEvents();
|
|
|
|
// If this actor is dead, call a stop to the calling process
|
|
if ((TinselVersion <= 1) && (actorId && !_vm->_actor->actorAlive(actorId)))
|
|
CORO_KILL_SELF();
|
|
|
|
if ((TinselVersion <= 1) || (speechType == IS_TALK)) {
|
|
/*
|
|
* Find out which actor is talking
|
|
* and with which direction if no film supplied
|
|
*/
|
|
switch (hFilm) {
|
|
case TF_NONE:
|
|
case TF_UP:
|
|
case TF_DOWN:
|
|
case TF_LEFT:
|
|
case TF_RIGHT:
|
|
_ctx->actor = _vm->_actor->GetLeadId(); // If no film, actor is lead actor
|
|
_ctx->direction = (TFTYPE)hFilm;
|
|
break;
|
|
|
|
default:
|
|
_ctx->actor = ExtractActor(hFilm);
|
|
assert(_ctx->actor); // talk() - no actor ID in the reel
|
|
_ctx->direction = TF_FILM;
|
|
break;
|
|
}
|
|
assert(_ctx->actor);
|
|
} else if (TinselVersion >= 2)
|
|
_ctx->actor = actorId;
|
|
|
|
/*
|
|
* Lock out the user (for lead actor, if not already locked out)
|
|
* May need to disable tags for other actors
|
|
*/
|
|
if (_ctx->actor == _vm->_actor->GetLeadId() || ((TinselVersion >= 2) && (_ctx->actor == LEAD_ACTOR)))
|
|
_ctx->bTookControl = GetControl(CONTROL_OFF);
|
|
else
|
|
_ctx->bTookControl = false;
|
|
_ctx->bTookTags = DisableTagsIfEnabled();
|
|
|
|
if (TinselVersion >= 2) {
|
|
/*
|
|
* Divert stuff
|
|
*/
|
|
if (SysVar(ISV_DIVERT_ACTOR) && (_ctx->actor == _vm->_actor->GetLeadId() || _ctx->actor == LEAD_ACTOR)) {
|
|
_ctx->actor = SysVar(ISV_DIVERT_ACTOR);
|
|
if (_ctx->whatSort == IS_TALK)
|
|
_ctx->whatSort = IS_SAY;
|
|
else if (_ctx->whatSort == IS_TALKAT)
|
|
_ctx->whatSort = IS_SAYAT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Kick off the voice sample
|
|
*/
|
|
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(hText)) {
|
|
if (TinselVersion <= 1) {
|
|
_vm->_sound->playSample(hText, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
|
|
_ctx->bSamples = _vm->_mixer->isSoundHandleActive(_ctx->handle);
|
|
} else
|
|
_ctx->bSamples = true;
|
|
} else
|
|
_ctx->bSamples = false;
|
|
|
|
/*
|
|
* Replace actor with the talk reel, saving the current one
|
|
*/
|
|
_ctx->pActor = GetMover(_ctx->actor);
|
|
if (_ctx->whatSort == IS_TALK) {
|
|
if (_ctx->pActor) {
|
|
if (_ctx->direction != TF_FILM)
|
|
hFilm = GetMoverTalkReel(_ctx->pActor, _ctx->direction);
|
|
AlterMover(_ctx->pActor, hFilm, AR_PUSHREEL);
|
|
} else {
|
|
_vm->_actor->SetActorTalking(_ctx->actor, true);
|
|
_vm->_actor->SetActorTalkFilm(_ctx->actor, hFilm);
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, -1, -1, 0, false, 0, escOn, myEscape, false, _vm->_bg->GetPlayfieldList(FIELD_WORLD)));
|
|
}
|
|
_ctx->bTalkReel = true;
|
|
CORO_SLEEP(1); // Allow the play to come in
|
|
|
|
} else if (_ctx->whatSort == IS_TALKAT) {
|
|
_ctx->bTalkReel = false;
|
|
|
|
} else if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
|
|
_ctx->bTalkReel = false;
|
|
if (_vm->_actor->IsTaggedActor(_ctx->actor)) {
|
|
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, TALKING, false, 0));
|
|
} else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY)) {
|
|
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, GetTagHandle(_ctx->actor | ACTORTAG_KEY),
|
|
TALKING, 0, false, 0));
|
|
}
|
|
|
|
if (TinselVersion >= 2)
|
|
// Let it all kick in and position this 'waiting' process
|
|
// down the process list from the playing process(es)
|
|
// This ensures immediate return when the reel finishes
|
|
CORO_GIVE_WAY;
|
|
}
|
|
|
|
// Make multi-ones escape
|
|
if ((TinselVersion >= 2) && (SubStringCount(hText) > 1) && !_ctx->escEvents)
|
|
_ctx->escEvents = GetEscEvents();
|
|
|
|
for (_ctx->sub = 0; _ctx->sub < ((TinselVersion >= 2) ? SubStringCount(hText) : 1); _ctx->sub++) {
|
|
if ((TinselVersion >= 2) && _ctx->escEvents && _ctx->escEvents != GetEscEvents())
|
|
break;
|
|
|
|
/*
|
|
* Display the text.
|
|
*/
|
|
_ctx->bSample = _ctx->bSamples;
|
|
_ctx->pText = nullptr;
|
|
|
|
if (_vm->_config->isJapanMode()) {
|
|
_ctx->ticks = JAP_TEXT_TIME;
|
|
} else if (_vm->_config->_useSubtitles || !_ctx->bSample) {
|
|
/*
|
|
* Work out where to display the text
|
|
*/
|
|
int xshift, yshift;
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
|
|
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK))
|
|
_vm->_actor->GetActorMidTop(_ctx->actor, &_ctx->x, &_ctx->y);
|
|
|
|
if ((TinselVersion != 0) && (TinselVersion != 3)) {
|
|
SetTextPal(_vm->_actor->GetActorRGB(_ctx->actor));
|
|
}
|
|
if (TinselVersion >= 2) {
|
|
LoadSubString(hText, _ctx->sub, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
} else {
|
|
LoadStringRes(hText, _vm->_font->TextBufferAddr(), TBUFSZ);
|
|
|
|
_ctx->y -= _ctx->Toffset;
|
|
}
|
|
|
|
int color = 0;
|
|
if (TinselVersion == 3) {
|
|
color = _vm->_actor->GetActorRGB(_ctx->actor);
|
|
}
|
|
|
|
_ctx->pText = ObjectTextOut(_vm->_bg->GetPlayfieldList(FIELD_STATUS),
|
|
_vm->_font->TextBufferAddr(), color, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
|
|
_vm->_font->GetTalkFontHandle(), TXT_CENTER);
|
|
assert(_ctx->pText); // talk() string produced NULL text;
|
|
|
|
if (_vm->_dialogs->isTopWindow())
|
|
MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
|
|
|
|
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK)) {
|
|
/*
|
|
* Set bottom of text just above the speaker's head
|
|
* But don't go off the top of the screen
|
|
*/
|
|
if (TinselVersion >= 2)
|
|
MultiMoveRelXY(_ctx->pText, 0, _ctx->y - _ctx->Toffset - MultiLowest(_ctx->pText) - 2);
|
|
else {
|
|
yshift = _ctx->y - MultiLowest(_ctx->pText) - 2; // Just above head
|
|
MultiMoveRelXY(_ctx->pText, 0, yshift); //
|
|
yshift = MultiHighest(_ctx->pText);
|
|
if (yshift < 4)
|
|
MultiMoveRelXY(_ctx->pText, 0, 4 - yshift); // Not off top
|
|
|
|
/*
|
|
* Don't go off the side of the screen
|
|
*/
|
|
xshift = MultiRightmost(_ctx->pText) + 2;
|
|
if (xshift >= SCREEN_WIDTH) // Not off right
|
|
MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0);
|
|
xshift = MultiLeftmost(_ctx->pText) - 1;
|
|
if (xshift <= 0) // Not off left
|
|
MultiMoveRelXY(_ctx->pText, -xshift, 0);
|
|
}
|
|
}
|
|
|
|
if (TinselVersion >= 2)
|
|
// Don't go off the screen
|
|
KeepOnScreen(_ctx->pText, &_ctx->x, &_ctx->y);
|
|
|
|
/*
|
|
* Work out how long to talk.
|
|
* During this time, reposition the text if the screen scrolls.
|
|
*/
|
|
_ctx->ticks = TextTime(_vm->_font->TextBufferAddr());
|
|
}
|
|
|
|
if ((TinselVersion >= 2) && _ctx->bSample) {
|
|
// Kick off the sample now (perhaps with a delay)
|
|
if (g_bNoPause)
|
|
g_bNoPause = false;
|
|
else if (!TinselV2Demo)
|
|
CORO_SLEEP(SysVar(SV_SPEECHDELAY));
|
|
|
|
//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
|
|
_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
|
|
}
|
|
|
|
_ctx->timeout = SAMPLETIMEOUT;
|
|
|
|
do {
|
|
// Keep text in place if scrolling
|
|
if (_ctx->pText != NULL) {
|
|
int nLoff, nToff;
|
|
|
|
_vm->_bg->PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
|
|
if (nLoff != _ctx->Loffset || nToff != _ctx->Toffset) {
|
|
MultiMoveRelXY(_ctx->pText, _ctx->Loffset - nLoff, _ctx->Toffset - nToff);
|
|
_ctx->Loffset = nLoff;
|
|
_ctx->Toffset = nToff;
|
|
}
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Handle timeout decrementing and Escape presses
|
|
if (TinselVersion >= 2) {
|
|
if ((_ctx->escEvents && _ctx->escEvents != GetEscEvents()) ||
|
|
(!bSustain && LeftEventChange(_ctx->myLeftEvent)) ||
|
|
(--_ctx->timeout <= 0)) {
|
|
// Left event only kills current sub-string
|
|
_ctx->myLeftEvent = GetLeftEvents();
|
|
break;
|
|
}
|
|
} else {
|
|
--_ctx->timeout;
|
|
|
|
// Abort if escapable and ESCAPE is pressed
|
|
// Abort if left click - hardwired feature for talk!
|
|
// Abort if sample times out
|
|
if ((escOn && myEscape != GetEscEvents())
|
|
|| (_ctx->myLeftEvent != GetLeftEvents())
|
|
|| (_ctx->timeout <= 0))
|
|
break;
|
|
}
|
|
|
|
if (_ctx->bSample) {
|
|
// Wait for sample to end whether or not
|
|
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
|
|
if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
|
|
// No text or speed modification - just depends on sample
|
|
break;
|
|
} else {
|
|
// Talk reel stops at end of speech
|
|
if ((TinselVersion <= 1) || (_ctx->bTalkReel && (_ctx->sub == SubStringCount(hText) - 1))) {
|
|
CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
|
|
_ctx->bTalkReel = false;
|
|
}
|
|
_ctx->bSample = false;
|
|
}
|
|
}
|
|
|
|
// Decrement the subtitles timeout counter
|
|
if (_ctx->ticks > 0) --_ctx->ticks;
|
|
|
|
} else {
|
|
// No sample - just depends on time
|
|
if (_ctx->ticks-- <= 0)
|
|
break;
|
|
}
|
|
} while (1);
|
|
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
|
|
|
if ((TinselVersion >= 2) && _ctx->bSample)
|
|
_vm->_sound->stopSpecSample(hText, _ctx->sub);
|
|
}
|
|
|
|
/*
|
|
* The talk is over now - dump the text
|
|
* Stop the sample
|
|
* Restore the actor's film or standing reel
|
|
*/
|
|
if (_ctx->bTalkReel)
|
|
CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_ctx->pText);
|
|
|
|
if (TinselVersion >= 2) {
|
|
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
|
|
_vm->_actor->SetActorTalking(_ctx->actor, false);
|
|
if (_vm->_actor->IsTaggedActor(_ctx->actor))
|
|
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, ENDTALK, false, 0));
|
|
else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY))
|
|
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX,
|
|
GetTagHandle(_ctx->actor | ACTORTAG_KEY), ENDTALK, 0, false, 0));
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
} else {
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
}
|
|
|
|
/*
|
|
* Restore user control and tags, as appropriate
|
|
* And, finally, release the talk token.
|
|
*/
|
|
if (_ctx->bTookControl) {
|
|
if (TinselVersion >= 2) ControlOn(); else Control(CONTROL_ON);
|
|
}
|
|
if (_ctx->bTookTags)
|
|
EnableTags();
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* TalkAt(actor, x, y, text)
|
|
*/
|
|
static void TalkAt(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myEscape) {
|
|
if (!coroParam) {
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (!_vm->_config->isJapanMode() && (_vm->_config->_useSubtitles || !_vm->_sound->sampleExists(text)))
|
|
SetTextPal(_vm->_actor->GetActorRGB(actor));
|
|
}
|
|
|
|
Print(coroParam, x, y, text, 0, false, escOn, myEscape);
|
|
}
|
|
|
|
/**
|
|
* TalkAtS(actor, x, y, text, sustain)
|
|
*/
|
|
static void TalkAtS(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myEscape) {
|
|
if (!coroParam) {
|
|
assert(sustain == 2);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (!_vm->_config->isJapanMode())
|
|
SetTextPal(_vm->_actor->GetActorRGB(actor));
|
|
}
|
|
|
|
Print(coroParam, x, y, text, 0, sustain == 2, escOn, myEscape);
|
|
}
|
|
|
|
/**
|
|
* Set talk font's palette entry.
|
|
*/
|
|
static void TalkAttr(int r1, int g1, int b1, bool escOn, int myEscape) {
|
|
if (_vm->_config->isJapanMode())
|
|
return;
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (r1 > MAX_INTENSITY) r1 = MAX_INTENSITY; // } Ensure
|
|
if (g1 > MAX_INTENSITY) g1 = MAX_INTENSITY; // } within limits
|
|
if (b1 > MAX_INTENSITY) b1 = MAX_INTENSITY; // }
|
|
|
|
SetTextPal(TINSEL_RGB(r1, g1, b1));
|
|
}
|
|
|
|
/**
|
|
* TalkPaletteIndex
|
|
*/
|
|
static void TalkPaletteIndex(unsigned index) {
|
|
assert(index);
|
|
|
|
SetTalkTextOffset(index);
|
|
}
|
|
|
|
/**
|
|
* Set talk font's palette entry.
|
|
*/
|
|
static void TalkRGB(COLORREF color, int myescEvent) {
|
|
// Don't do it if it's not wanted
|
|
if (myescEvent && myescEvent != GetEscEvents())
|
|
return;
|
|
|
|
SetTextPal(color);
|
|
}
|
|
|
|
/**
|
|
* TalkVia("actor"/off)
|
|
*/
|
|
static void TalkVia(int actor) {
|
|
SetSysVar(ISV_DIVERT_ACTOR, actor);
|
|
}
|
|
|
|
/**
|
|
* ThisObject
|
|
*/
|
|
static int ThisObject(const InventoryObject *pinvo) {
|
|
assert(pinvo != NULL);
|
|
|
|
return pinvo->getId();
|
|
}
|
|
|
|
/**
|
|
* ThisTag
|
|
*/
|
|
static int ThisTag(HPOLYGON hp) {
|
|
int tagno;
|
|
|
|
assert(hp != NOPOLY);
|
|
|
|
tagno = GetTagPolyId(hp);
|
|
|
|
assert(IsTagPolygon(tagno));
|
|
assert(tagno);
|
|
|
|
return tagno;
|
|
}
|
|
|
|
/**
|
|
* Get a timer's current count.
|
|
*/
|
|
static int TimerFn(int timerno) {
|
|
return Timer(timerno);
|
|
}
|
|
|
|
/**
|
|
* Return the icon that caused the CONVERSE event.
|
|
*/
|
|
int Topic() {
|
|
return _vm->_dialogs->getIcon();
|
|
}
|
|
|
|
/**
|
|
* topplay(film, x, y, actor, hold, complete)
|
|
*/
|
|
static void TopPlay(CORO_PARAM, SCNHANDLE film, int x, int y, int complete, int actorid, bool splay, int sfact, bool escOn, int myescTime) {
|
|
Play(coroParam, film, x, y, complete, actorid, splay, sfact, escOn, myescTime, true);
|
|
}
|
|
static void TopPlay(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myescEvent, TINSEL_EVENT event) {
|
|
Play(coroParam, hFilm, x, y, bComplete, myescEvent, true, event, NOPOLY, 0);
|
|
}
|
|
|
|
/**
|
|
* Open or close the 'top window'
|
|
*/
|
|
static void TopWindow(int bpos) {
|
|
bool isStart = ((TinselVersion >= 2) && (bpos != 0)) || ((TinselVersion <= 1) && (bpos == TW_START));
|
|
|
|
_vm->_dialogs->killInventory();
|
|
|
|
if (isStart)
|
|
_vm->_dialogs->openMenu(TOP_WINDOW);
|
|
}
|
|
|
|
/**
|
|
* TranslucentIndex
|
|
*/
|
|
static void TranslucentIndex(unsigned index) {
|
|
assert(index <= 255);
|
|
|
|
SetTranslucencyOffset(index);
|
|
}
|
|
|
|
/**
|
|
* Play a sample (DW1 only).
|
|
*/
|
|
static void TryPlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
// Don't do it if it's not appropriate
|
|
if (_vm->_sound->sampleIsPlaying()) {
|
|
// return, but prevent Glitter lock-up
|
|
CORO_SLEEP(1);
|
|
return;
|
|
}
|
|
|
|
CORO_INVOKE_ARGS(PlaySample, (CORO_SUBCTX, sample, bComplete, escOn, myEscape));
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Un-define an actor as tagged.
|
|
*/
|
|
static void UnTagActorFn(int actor) {
|
|
_vm->_actor->UnTagActor(actor);
|
|
}
|
|
|
|
/**
|
|
* vibrate
|
|
*/
|
|
static void Vibrate() {
|
|
}
|
|
|
|
/**
|
|
* waitframe(int actor, int frameNumber)
|
|
*/
|
|
static void WaitFrame(CORO_PARAM, int actor, int frameNumber, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
while (_vm->_actor->GetActorSteps(actor) < frameNumber) {
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
break;
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Return when a key pressed or button pushed.
|
|
*/
|
|
static void WaitKey(CORO_PARAM, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int startEvent;
|
|
int startX, startY;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
for (;;) {
|
|
_ctx->startEvent = getUserEvents();
|
|
if (TinselVersion == 1) {
|
|
// Store cursor position
|
|
while (!_vm->_cursor->GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false))
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
while (_ctx->startEvent == getUserEvents()) {
|
|
CORO_SLEEP(1);
|
|
|
|
// Not necessary to monitor escape as it's an event anyway
|
|
if (TinselVersion == 1) {
|
|
int curX, curY;
|
|
_vm->_cursor->GetCursorXY(&curX, &curY, false); // Store cursor position
|
|
if (curX != _ctx->startX || curY != _ctx->startY)
|
|
break;
|
|
}
|
|
|
|
if (_vm->_dialogs->menuActive())
|
|
break;
|
|
}
|
|
|
|
if (!_vm->_dialogs->menuActive())
|
|
return;
|
|
|
|
do {
|
|
CORO_SLEEP(1);
|
|
} while (_vm->_dialogs->menuActive());
|
|
|
|
CORO_SLEEP(ONE_SECOND / 2); // Let it die down
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Return when no scrolling is going on.
|
|
*/
|
|
void WaitScroll(CORO_PARAM, int myescEvent) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int time;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// wait for ongoing scroll
|
|
while (_vm->_scroll->IsScrolling()) {
|
|
if (myescEvent && myescEvent != GetEscEvents())
|
|
break;
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Pause for requested time.
|
|
*/
|
|
static void WaitTime(CORO_PARAM, int time, bool frame, bool escOn, int myEscape) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int time;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (!frame)
|
|
time *= ONE_SECOND;
|
|
|
|
_ctx->time = time;
|
|
do {
|
|
CORO_SLEEP(1);
|
|
|
|
// Abort if escapable and ESCAPE is pressed
|
|
if (escOn && myEscape != GetEscEvents())
|
|
break;
|
|
} while (_ctx->time--);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Set a moving actor off on a walk.
|
|
*/
|
|
void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm, int hold, bool igPath,
|
|
int zOverride, bool escOn, int myescEvent) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int thisWalk;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
bool bQuick = hold != 0;
|
|
MOVER *pMover = GetMover(actor);
|
|
|
|
assert(pMover); // Can't walk a non-moving actor
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Straight there if escaped
|
|
if (escOn && myescEvent != GetEscEvents()) {
|
|
if (TinselVersion >= 2)
|
|
StopMover(pMover);
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
return;
|
|
}
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
|
|
// Test 10/10/96
|
|
while (!MoverIs(pMover))
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
assert(pMover->hCpath != NOPOLY); // moving actor not in path
|
|
|
|
// Croak if he is doing an SWalk()
|
|
if (TinselVersion >= 2) {
|
|
// Croak if he is doing an SWalk()
|
|
if (MoverIsSWalking(pMover))
|
|
CORO_KILL_SELF();
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, x, y, igPath, hFilm);
|
|
SetMoverZoverride(pMover, zOverride);
|
|
_vm->_scroll->DontScrollCursor();
|
|
|
|
if (!bQuick) {
|
|
while (MoverMoving(pMover)) {
|
|
// Straight there if escaped
|
|
if (escOn && myescEvent != GetEscEvents()) {
|
|
StopMover(pMover);
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
break;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Die if superceded
|
|
if (_ctx->thisWalk != GetWalkNumber(pMover))
|
|
CORO_KILL_SELF();
|
|
}
|
|
}
|
|
} else {
|
|
|
|
GetToken(pMover->actorToken);
|
|
SetActorDest(pMover, x, y, igPath, hFilm);
|
|
_vm->_scroll->DontScrollCursor();
|
|
|
|
if (hold == 2) {
|
|
;
|
|
} else {
|
|
while (MoverMoving(pMover)) {
|
|
CORO_SLEEP(1);
|
|
|
|
// Straight there if escaped
|
|
if (escOn && myescEvent != GetEscEvents()) {
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
FreeToken(pMover->actorToken);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeToken(pMover->actorToken);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Set a moving actor off on a walk.
|
|
* Wait to see if its aborted or completed.
|
|
*/
|
|
static void Walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myEscape, bool &retVal) {
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
int thisWalk;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover); // Can't walk a non-moving actor
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Straight there if escaped
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
retVal = true;
|
|
return;
|
|
}
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (MoverHidden(pMover) || !MoverIs(pMover)) {
|
|
retVal = false;
|
|
return;
|
|
}
|
|
assert(pMover->hCpath != NOPOLY); // moving actor not in path
|
|
|
|
// Not if he is doing an SWalk()
|
|
if (MoverIsSWalking(pMover)) {
|
|
retVal = false;
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
// Pause before starting the walk
|
|
CORO_SLEEP(ONE_SECOND);
|
|
|
|
assert(pMover->hCpath != NOPOLY); // moving actor not in path
|
|
|
|
// Briefly acquire token to kill off any other normal walk
|
|
GetToken(pMover->actorToken);
|
|
FreeToken(pMover->actorToken);
|
|
}
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, x, y, false, film);
|
|
_vm->_scroll->DontScrollCursor();
|
|
|
|
while (MoverMoving(pMover) && (_ctx->thisWalk == GetWalkNumber(pMover))) {
|
|
// Straight there if escaped
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
retVal = true;
|
|
return;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
}
|
|
|
|
int endx, endy;
|
|
GetMoverPosition(pMover, &endx, &endy);
|
|
retVal = (_ctx->thisWalk == GetWalkNumber(pMover) && endx == x && endy == y);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Declare a moving actor.
|
|
*/
|
|
static void WalkingActor(uint32 id, SCNHANDLE *rp = NULL) {
|
|
MOVER *pActor; // Moving actor structure
|
|
|
|
if (TinselVersion == 2) {
|
|
RegisterMover(id);
|
|
return;
|
|
}
|
|
|
|
RegisterMover(id); // Establish as a moving actor
|
|
pActor = GetMover(id);
|
|
assert(pActor);
|
|
|
|
// Store all those reels
|
|
int i, j;
|
|
for (i = 0; i < 5; ++i) {
|
|
for (j = 0; j < 4; ++j)
|
|
pActor->walkReels[i][j] = *rp++;
|
|
for (j = 0; j < 4; ++j)
|
|
pActor->standReels[i][j] = *rp++;
|
|
}
|
|
|
|
|
|
for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
|
|
for (j = 0; j < 4; ++j) {
|
|
pActor->walkReels[i][j] = pActor->walkReels[4][j];
|
|
pActor->standReels[i][j] = pActor->standReels[2][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walk a moving actor towards the polygon's tag, but return when the
|
|
* actor enters the polygon.
|
|
*/
|
|
static void WalkPoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
|
|
int pnodex, pnodey;
|
|
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
int thisWalk;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
assert(hp != NOPOLY); // WalkPoly() may only be called from a polygon code block
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover); // Can't walk a non-moving actor
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Straight there if escaped
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
StandTag(actor, hp);
|
|
return;
|
|
}
|
|
|
|
if (TinselVersion >= 2) {
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
|
|
// Croak if he is doing an SWalk()
|
|
if (MoverIsSWalking(pMover))
|
|
CORO_KILL_SELF();
|
|
|
|
} else {
|
|
GetToken(pMover->actorToken);
|
|
}
|
|
|
|
GetPolyNode(hp, &pnodex, &pnodey);
|
|
_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
|
|
_vm->_scroll->DoScrollCursor();
|
|
|
|
while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) {
|
|
CORO_SLEEP(1);
|
|
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
// Straight there if escaped
|
|
StandTag(actor, hp);
|
|
if (TinselVersion <= 1)
|
|
FreeToken(pMover->actorToken);
|
|
return;
|
|
}
|
|
|
|
// Die if superceded
|
|
if ((TinselVersion >= 2) && (_ctx->thisWalk != GetWalkNumber(pMover)))
|
|
CORO_KILL_SELF();
|
|
}
|
|
|
|
if (TinselVersion <= 1)
|
|
FreeToken(pMover->actorToken);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* WalkTag(actor, reel, hold)
|
|
*/
|
|
static void WalkTag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
|
|
// COROUTINE
|
|
CORO_BEGIN_CONTEXT;
|
|
int thisWalk;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
MOVER *pMover = GetMover(actor);
|
|
assert(pMover); // Can't walk a non-moving actor
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
int pnodex, pnodey;
|
|
|
|
assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block
|
|
|
|
// Straight there if escaped
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
StandTag(actor, hp);
|
|
return;
|
|
}
|
|
|
|
if (TinselVersion <= 1)
|
|
GetToken(pMover->actorToken);
|
|
else {
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
}
|
|
|
|
GetPolyNode(hp, &pnodex, &pnodey);
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
|
|
_vm->_scroll->DoScrollCursor();
|
|
|
|
while (MoverMoving(pMover)) {
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
// Straight there if escaped
|
|
StandTag(actor, hp);
|
|
if (TinselVersion <= 1)
|
|
FreeToken(pMover->actorToken);
|
|
return;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Die if superceded
|
|
if ((TinselVersion >= 2) && (_ctx->thisWalk != GetWalkNumber(pMover)))
|
|
CORO_KILL_SELF();
|
|
}
|
|
|
|
// Adopt the tag-related reel
|
|
SCNHANDLE pFilm = GetPolyFilm(hp);
|
|
|
|
switch (pFilm) {
|
|
case TF_NONE:
|
|
break;
|
|
|
|
case TF_UP:
|
|
SetMoverDirection(pMover, AWAY);
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
case TF_DOWN:
|
|
SetMoverDirection(pMover, FORWARD);
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
case TF_LEFT:
|
|
SetMoverDirection(pMover, LEFTREEL);
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
case TF_RIGHT:
|
|
SetMoverDirection(pMover, RIGHTREEL);
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
|
|
default:
|
|
if (actor == LEAD_ACTOR || actor == _vm->_actor->GetLeadId())
|
|
AlterMover(pMover, pFilm, AR_NORMAL);
|
|
else
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
}
|
|
|
|
if (TinselVersion <= 1)
|
|
FreeToken(pMover->actorToken);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Returns the X co-ordinateof lead actor's last walk.
|
|
*/
|
|
int WalkXPos() {
|
|
return GetLastLeadXdest();
|
|
}
|
|
|
|
/**
|
|
* Returns the Y co-ordinateof lead actor's last walk.
|
|
*/
|
|
int WalkYPos() {
|
|
return GetLastLeadYdest();
|
|
}
|
|
|
|
/**
|
|
* Return which is the current CD, counting from 1.
|
|
*/
|
|
int WhichCd() {
|
|
return GetCurrentCD();
|
|
}
|
|
|
|
/**
|
|
* whichinventory
|
|
*/
|
|
int WhichInventory() {
|
|
return _vm->_dialogs->whichInventoryOpen();
|
|
}
|
|
|
|
|
|
struct NoirMapping {
|
|
const char *name;
|
|
int libCode;
|
|
int numArgs;
|
|
};
|
|
|
|
NoirMapping translateNoirLibCode(int libCode, int32 *pp) {
|
|
// This function allows us to both log the called library functions, as well
|
|
// as to stub the ones we haven't yet implemented. Eventually this might
|
|
// get rolled up into a lookup table similar to DW1 and DW2, but for now
|
|
// this is convenient for debug.
|
|
NoirMapping mapping;
|
|
switch (libCode) {
|
|
case 0:
|
|
error("NoFunction isn't a real function");
|
|
break;
|
|
case 1:
|
|
mapping = NoirMapping{"ACTORBRIGHTNESS", ACTORBRIGHTNESS, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 2:
|
|
mapping = NoirMapping{"ACTORDIRECTION", ACTORDIRECTION, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 3:
|
|
mapping = NoirMapping{"ACTORPRIORITY", ACTORPRIORITY, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 4:
|
|
mapping = NoirMapping{"ACTORREF", ACTORREF, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 5:
|
|
mapping = NoirMapping{"ACTORRGB", ACTORRGB, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 6:
|
|
mapping = NoirMapping{"ACTORXPOS", ACTORXPOS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 7:
|
|
mapping = NoirMapping{"ACTORYPOS", ACTORYPOS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 8:
|
|
mapping = NoirMapping{"ADDNOTEBOOK", ADDNOTEBOOK, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 9:
|
|
mapping = NoirMapping{"ADDCONV", ADDCONV, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 10:
|
|
mapping = NoirMapping{"ADDHIGHLIGHT", ADDHIGHLIGHT, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 11:
|
|
error("Unsupported libCode %d ADDINV 8", libCode);
|
|
case 12:
|
|
mapping = NoirMapping{"ADDINV1", ADDINV1, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 13:
|
|
mapping = NoirMapping{"ADDINV2", ADDINV2, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 14:
|
|
error("Unsupported libCode %d ADDINV 7", libCode);
|
|
case 15:
|
|
error("Unsupported libCode %d ADDINV 4", libCode);
|
|
case 16:
|
|
mapping = NoirMapping{"ADDINV3", ADDINV3, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 17:
|
|
mapping = NoirMapping{"ADDTOPIC", ADDTOPIC, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 18:
|
|
mapping = NoirMapping{"BACKGROUND", BACKGROUND, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 19: // 1 param, calls SetSysVar(ISV_NO_BLOCKING_T3, param == 0)
|
|
error("Unsupported libCode %d set_isv_blocking", libCode);
|
|
case 20: // 3 params, assigns values to three globals
|
|
error("Unsupported libCode %d", libCode);
|
|
case 21:
|
|
mapping = NoirMapping{"CALLACTOR", CALLACTOR, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 22:
|
|
mapping = NoirMapping{"CALLGLOBALPROCESS", CALLGLOBALPROCESS, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 23:
|
|
mapping = NoirMapping{"CALLOBJECT", CALLOBJECT, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 24:
|
|
mapping = NoirMapping{"CALLPROCESS", CALLPROCESS, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 25:
|
|
mapping = NoirMapping{"CALLSCENE", CALLSCENE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 26:
|
|
mapping = NoirMapping{"CALLTAG", CALLTAG, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 27:
|
|
mapping = NoirMapping{"CAMERA", CAMERA, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 28:
|
|
mapping = NoirMapping{"CDCHANGESCENE", CDCHANGESCENE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 29:
|
|
mapping = NoirMapping{"CDDOCHANGE", CDDOCHANGE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 30:
|
|
mapping = NoirMapping{"CDENDACTOR", CDENDACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 31:
|
|
mapping = NoirMapping{"CDLOAD", CDLOAD, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 32:
|
|
mapping = NoirMapping{"CDPLAY", CDPLAY, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 33: // Disassembly just returns -1 for this opcode, so map it to ZZZZZZ
|
|
mapping = NoirMapping{"OP33_NOP", ZZZZZZ, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 34:
|
|
mapping = NoirMapping{"CLEARHOOKSCENE", CLEARHOOKSCENE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 35: // 0 params, closes open inventory if it is 1 or 2
|
|
error("Unsupported libCode %d to close inventory", libCode);
|
|
case 36: // 0 params, closes open inventory if it is 3
|
|
error("Unsupported libCode %d to close inventory", libCode);
|
|
case 37:
|
|
mapping = NoirMapping{"CONTROL", CONTROL, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 38:
|
|
mapping = NoirMapping{"CONVERSATION", CONVERSATION, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 39: // 1 param
|
|
mapping = NoirMapping{"CROSSCLUE", CROSSCLUE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 40:
|
|
mapping = NoirMapping{"CURSOR", CURSOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 41:
|
|
mapping = NoirMapping{"CURSORXPOS", CURSORXPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 42:
|
|
mapping = NoirMapping{"CURSORYPOS", CURSORYPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 43:
|
|
mapping = NoirMapping{"DECINVMAIN", DECINVMAIN, 8};
|
|
pp -= mapping.numArgs - 1;
|
|
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]);
|
|
break;
|
|
case 44: // Changed in Noir
|
|
mapping = NoirMapping{"DECINV2", DECINV2, 8};
|
|
pp -= mapping.numArgs - 1;
|
|
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]);
|
|
break;
|
|
case 45:
|
|
mapping = NoirMapping{"DECLARELANGUAGE", DECLARELANGUAGE, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 46:
|
|
mapping = NoirMapping{"DECLEAD", DECLEAD, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 47:
|
|
mapping = NoirMapping{"DEC3D", DEC3D, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 48:
|
|
mapping = NoirMapping{"DECTAGFONT", DECTAGFONT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 49:
|
|
mapping = NoirMapping{"DECTALKFONT", DECTALKFONT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 50: // 1 parameter, calls remFromInventory variant of TinselV1
|
|
error("Unsupported libCode %d del_inv3_item", libCode);
|
|
case 51: // 1 parameter
|
|
error("Unsupported libCode %d DELTOPIC variant", libCode);
|
|
case 52:
|
|
mapping = NoirMapping{"DIMMUSIC", DIMMUSIC, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 53:
|
|
mapping = NoirMapping{"DROP", DROP, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 54:
|
|
mapping = NoirMapping{"DROPEVERYTHING", DROPEVERYTHING, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 55:
|
|
mapping = NoirMapping{"DROPOUT", DROPOUT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 56:
|
|
mapping = NoirMapping{"EFFECTACTOR", EFFECTACTOR, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 57:
|
|
mapping = NoirMapping{"ENABLEMENU", ENABLEMENU, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 58:
|
|
mapping = NoirMapping{"ENDACTOR", ENDACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 59:
|
|
mapping = NoirMapping{"ESCAPEOFF", ESCAPEOFF, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 60:
|
|
mapping = NoirMapping{"ESCAPEON", ESCAPEON, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 61:
|
|
mapping = NoirMapping{"EVENT", EVENT, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 62:
|
|
mapping = NoirMapping{"FACETAG", FACETAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 63:
|
|
mapping = NoirMapping{"FADEIN", FADEIN, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 64:
|
|
mapping = NoirMapping{"FADEMUSIC", FADEMUSIC, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%08X, %d)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 65:
|
|
mapping = NoirMapping{"FADEOUT", FADEOUT, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 66: // Disassembly just returns -1 for this opcode, so map it to ZZZZZZ
|
|
mapping = NoirMapping{"OP66_NOP", ZZZZZZ, 1};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 67:
|
|
mapping = NoirMapping{"FREEZECURSOR", FREEZECURSOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 68:
|
|
mapping = NoirMapping{"GETINVLIMIT", GETINVLIMIT, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 69:
|
|
mapping = NoirMapping{"GHOST", GHOST, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 70:
|
|
mapping = NoirMapping{"GLOBALVAR", GLOBALVAR, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 71:
|
|
error("You'll have to ask John to put this back in!");
|
|
case 72:
|
|
mapping = NoirMapping{"HAILSCENE", HAILSCENE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 73:
|
|
mapping = NoirMapping{"HASRESTARTED", HASRESTARTED, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 74:
|
|
mapping = NoirMapping{"HAVE", HAVE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 75:
|
|
mapping = NoirMapping{"HELDOBJECTORTOPIC", HELDOBJECTORTOPIC, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 76: // 0 parameters, returns enum depending on a bitfield value on held object
|
|
error("Unsupported libCode %d", libCode);
|
|
case 77:
|
|
mapping = NoirMapping{"HIDEACTOR", HIDEACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 78:
|
|
mapping = NoirMapping{"HIDEBLOCK", HIDEBLOCK, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 79:
|
|
mapping = NoirMapping{"HIDEEFFECT", HIDEEFFECT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 80:
|
|
mapping = NoirMapping{"HIDEPATH", HIDEPATH, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 81:
|
|
mapping = NoirMapping{"HIDEREFER", HIDEREFER, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 82: // hide poly type 6
|
|
error("Unsupported libCode %d hide_poly_type_6", libCode);
|
|
case 83:
|
|
mapping = NoirMapping{"HIDETAG", HIDETAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 84:
|
|
mapping = NoirMapping{"HOLD", HOLD, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 85:
|
|
mapping = NoirMapping{"HOOKSCENE", HOOKSCENE, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 86: // 2 parameters
|
|
mapping = NoirMapping{"BOOKADDHYPERLINK", BOOKADDHYPERLINK, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
break;
|
|
case 87:
|
|
mapping = NoirMapping{"IDLETIME", IDLETIME, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 88:
|
|
mapping = NoirMapping{"INSTANTSCROLL", INSTANTSCROLL, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 89:
|
|
mapping = NoirMapping{"INVENTORY", INVENTORY, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 90: // 2 parameters, play anim based on item
|
|
mapping = NoirMapping{"INVPLAY", INVPLAY, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d %d)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 91:
|
|
mapping = NoirMapping{"INWHICHINV", INWHICHINV, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 92:
|
|
mapping = NoirMapping{"KILLACTOR", KILLACTOR, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 93:
|
|
mapping = NoirMapping{"KILLGLOBALPROCESS", KILLGLOBALPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 94:
|
|
mapping = NoirMapping{"KILLPROCESS", KILLPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 95:
|
|
mapping = NoirMapping{"LOCALVAR", LOCALVAR, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 96:
|
|
mapping = NoirMapping{"MOVECURSOR", MOVECURSOR, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, %d)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 97: // MoveTag with additional logic
|
|
error("Unsupported libCode %d MOVETAG variant", libCode);
|
|
case 98: // MoveTagTo with additional logic
|
|
error("Unsupported libCode %d MOVETAGTO variant", libCode);
|
|
case 99:
|
|
mapping = NoirMapping{"NEWSCENE", NEWSCENE, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 100:
|
|
mapping = NoirMapping{"NOBLOCKING", NOBLOCKING, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 101:
|
|
mapping = NoirMapping{"NOPAUSE", NOPAUSE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 102:
|
|
mapping = NoirMapping{"NOSCROLL", NOSCROLL, 4};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3]);
|
|
break;
|
|
case 103: // 0 parameters
|
|
mapping = NoirMapping{"OPENNOTEBOOK", OPENNOTEBOOK, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 104: // 1 parameter
|
|
error("Unsupported libCode %d OFFSET variant", libCode);
|
|
case 105: // 0 parameters
|
|
error("Unsupported libCode %d INVENTORY4", libCode);
|
|
case 106: // 0 parameters
|
|
warning("TODO: Implement INVENTORY3");
|
|
mapping = NoirMapping{"INVENTORY3", ZZZZZZ, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 107:
|
|
mapping = NoirMapping{"OTHEROBJECT", OTHEROBJECT, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 108:
|
|
mapping = NoirMapping{"PAUSE", PAUSE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 109:
|
|
mapping = NoirMapping{"OBJECTHELD", OBJECTHELD, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 110:
|
|
mapping = NoirMapping{"PLAY", PLAY, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 112:
|
|
mapping = NoirMapping{"PLAYMUSIC", PLAYMUSIC, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 113:
|
|
mapping = NoirMapping{"PLAYSAMPLE", PLAYSAMPLE, 4};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3]);
|
|
break;
|
|
case 114:
|
|
mapping = NoirMapping{"POINTACTOR", POINTACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 115:
|
|
mapping = NoirMapping{"POINTTAG", POINTTAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 116:
|
|
mapping = NoirMapping{"POSTACTOR", POSTACTOR, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 117:
|
|
error("Unsupported libCode %d POSTPOLY", libCode);
|
|
case 118:
|
|
mapping = NoirMapping{"POSTGLOBALPROCESS", POSTGLOBALPROCESS, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 119:
|
|
mapping = NoirMapping{"POSTOBJECT", POSTOBJECT, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 120:
|
|
mapping = NoirMapping{"POSTPROCESS", POSTPROCESS, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 121:
|
|
mapping = NoirMapping{"POSTTAG", POSTTAG, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 122: // Something movie-related
|
|
error("Unsupported libCode %d, movie-related", libCode);
|
|
case 123:
|
|
mapping = NoirMapping{"PRINT", PRINT, 5};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3], pp[4]);
|
|
break;
|
|
case 124:
|
|
mapping = NoirMapping{"PRINTCURSOR", PRINTCURSOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 125:
|
|
mapping = NoirMapping{"PRINTOBJ", PRINTOBJ, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 126:
|
|
mapping = NoirMapping{"PRINTTAG", PRINTTAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 127:
|
|
mapping = NoirMapping{"QUITGAME", QUITGAME, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 128:
|
|
mapping = NoirMapping{"RANDOM", RANDOM, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, %d, %d)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 129:
|
|
mapping = NoirMapping{"RESETIDLETIME", RESETIDLETIME, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 130:
|
|
mapping = NoirMapping{"RESTARTGAME", RESTARTGAME, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 131:
|
|
mapping = NoirMapping{"RESTORESCENE", RESTORESCENE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 132:
|
|
mapping = NoirMapping{"RESUMELASTGAME", RESUMELASTGAME, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 133:
|
|
mapping = NoirMapping{"RUNMODE", RUNMODE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 134:
|
|
mapping = NoirMapping{"SAVESCENE", SAVESCENE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 135:
|
|
case 221:
|
|
case 222:
|
|
case 223:
|
|
mapping = NoirMapping{"SAY", SAY, 2};
|
|
pp -= mapping.numArgs - 2;
|
|
debug(7, "%s_%2Xh(%d, %d)", mapping.name, libCode, pp[0], pp[1]);
|
|
break;
|
|
case 136:
|
|
mapping = NoirMapping{"SAYAT", SAYAT, 5};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3], pp[4]);
|
|
break;
|
|
case 137:
|
|
mapping = NoirMapping{"SCREENXPOS", SCREENXPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 138:
|
|
mapping = NoirMapping{"SCREENYPOS", SCREENYPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 139: // new scroll with 1, 3 or 5 parameters
|
|
error("Unsupported libCode %d SCROLL variant", libCode);
|
|
case 140:
|
|
mapping = NoirMapping{"SCROLLPARAMETERS", SCROLLPARAMETERS, 7};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(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]);
|
|
break;
|
|
case 141:
|
|
mapping = NoirMapping{"SENDACTOR", SENDACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 142:
|
|
mapping = NoirMapping{"SENDGLOBALPROCESS", SENDGLOBALPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 143:
|
|
mapping = NoirMapping{"SENDOBJECT", SENDOBJECT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 144:
|
|
mapping = NoirMapping{"SENDPROCESS", SENDPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 145:
|
|
mapping = NoirMapping{"SENDTAG", SENDTAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 146: //calls function which is a nop
|
|
mapping = NoirMapping{"OP146_NOP", ZZZZZZ, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 147:
|
|
mapping = NoirMapping{"SETINVLIMIT", SETINVLIMIT, 2};
|
|
pp -= mapping.numArgs - 2;
|
|
debug(7, "%s_%2Xh(%d, %d)", mapping.name, libCode, pp[0], pp[1]);
|
|
break;
|
|
case 148:
|
|
mapping = NoirMapping{"SETINVSIZE", SETINVSIZE, 7};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(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]);
|
|
break;
|
|
case 149:
|
|
mapping = NoirMapping{"SETLANGUAGE", SETLANGUAGE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 150: // 3 parameters, calls function calling GetEscEvents not changing state
|
|
mapping = NoirMapping{"OP150_NOP", ZZZZZZ, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 151:
|
|
mapping = NoirMapping{"SETSYSTEMREEL", SETSYSTEMREEL, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 152:
|
|
mapping = NoirMapping{"SETSYSTEMSTRING", SETSYSTEMSTRING, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, %08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 153:
|
|
mapping = NoirMapping{"SETSYSTEMVAR", SETSYSTEMVAR, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 154:
|
|
mapping = NoirMapping{"SETVIEW", SETVIEW, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 155:
|
|
mapping = NoirMapping{"SHELL", SHELL, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 156:
|
|
mapping = NoirMapping{"SHOWACTOR", SHOWACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 157:
|
|
mapping = NoirMapping{"SHOWBLOCK", SHOWBLOCK, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 158:
|
|
mapping = NoirMapping{"SHOWEFFECT", SHOWEFFECT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 159:
|
|
mapping = NoirMapping{"SHOWMENU", SHOWMENU, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 160:
|
|
mapping = NoirMapping{"SHOWPATH", SHOWPATH, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 161:
|
|
mapping = NoirMapping{"SHOWREFER", SHOWREFER, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 162: // show poly type 6
|
|
error("Unsupported libCode %d show_poly_type_6", libCode);
|
|
case 163:
|
|
mapping = NoirMapping{"SHOWTAG", SHOWTAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 164:
|
|
mapping = NoirMapping{"STAND", STAND, 4};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3]);
|
|
break;
|
|
case 165:
|
|
mapping = NoirMapping{"STANDTAG", STANDTAG, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 166:
|
|
mapping = NoirMapping{"STARTGLOBALPROCESS", STARTGLOBALPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 167:
|
|
mapping = NoirMapping{"STARTPROCESS", STARTPROCESS, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 168:
|
|
mapping = NoirMapping{"STARTTIMER", STARTTIMER, 4};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3]);
|
|
break;
|
|
case 169: // 0 parameters, passes
|
|
error("Unsupported libCode %d audio-related", libCode);
|
|
case 170:
|
|
mapping = NoirMapping{"STOPSAMPLE", STOPSAMPLE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 171:
|
|
mapping = NoirMapping{"STOPWALK", STOPWALK, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 172:
|
|
mapping = NoirMapping{"SUBTITLES", SUBTITLES, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 173:
|
|
mapping = NoirMapping{"SWALK", SWALK, 6};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(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]);
|
|
break;
|
|
case 174:
|
|
mapping = NoirMapping{"SWALKZ", SWALKZ, 7};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(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]);
|
|
break;
|
|
case 175:
|
|
mapping = NoirMapping{"SYSTEMVAR", SYSTEMVAR, 0};
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
// These 4 are a fallthrough in the disassembly
|
|
// Assume they still have the same order for clarity of log output
|
|
case 176:
|
|
mapping = NoirMapping{"TAGTAGXPOS", TAGTAGXPOS, 0};
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 177:
|
|
mapping = NoirMapping{"TAGTAGYPOS", TAGTAGYPOS, 0};
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 178:
|
|
mapping = NoirMapping{"TAGWALKXPOS", TAGWALKXPOS, 0};
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 179:
|
|
mapping = NoirMapping{"TAGWALKYPOS", TAGWALKYPOS, 0};
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 180: // 2 parameters, talkorsay has been modified vor v3
|
|
case 218:
|
|
case 219:
|
|
case 220:
|
|
error("Unsupported libCode %d TALK with unsupported speech type", libCode);
|
|
case 181: // talkorsay has been modified vor v3
|
|
warning("TODO: Implement TalkOrSay v3");
|
|
mapping = NoirMapping{"TALKAT", TALKAT, 5};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3], pp[4]);
|
|
break;
|
|
case 182:
|
|
mapping = NoirMapping{"TALKRGB", TALKRGB, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 183:
|
|
mapping = NoirMapping{"TALKVIA", TALKVIA, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 184:
|
|
mapping = NoirMapping{"TEMPTAGFONT", TEMPTAGFONT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 185:
|
|
mapping = NoirMapping{"TEMPTALKFONT", TEMPTALKFONT, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 186:
|
|
mapping = NoirMapping{"THISOBJECT", THISOBJECT, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 187:
|
|
mapping = NoirMapping{"THISTAG", THISTAG, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 188:
|
|
mapping = NoirMapping{"TIMER", TIMER, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 189:
|
|
mapping = NoirMapping{"TOPIC", TOPIC, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 190:
|
|
mapping = NoirMapping{"TOPPLAY", TOPPLAY, 4};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3]);
|
|
break;
|
|
case 191:
|
|
mapping = NoirMapping{"TOPWINDOW", TOPWINDOW, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d)", mapping.name, pp[0]);
|
|
break;
|
|
case 192:
|
|
mapping = NoirMapping{"UNDIMMUSIC", UNDIMMUSIC, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 193:
|
|
mapping = NoirMapping{"UNHOOKSCENE", UNHOOKSCENE, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 194:
|
|
mapping = NoirMapping{"WAITFRAME", WAITFRAME, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, %d)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 195:
|
|
mapping = NoirMapping{"WAITKEY", WAITKEY, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 196:
|
|
mapping = NoirMapping{"WAITSCROLL", WAITSCROLL, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 197:
|
|
mapping = NoirMapping{"WAITTIME", WAITTIME, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(%d, %d)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 198:
|
|
mapping = NoirMapping{"WALK", WALK, 5};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2], pp[3], pp[4]);
|
|
break;
|
|
case 199:
|
|
mapping = NoirMapping{"WALKED", WALKED, 3};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X, 0x%08X)", mapping.name, pp[0], pp[1], pp[2]);
|
|
break;
|
|
case 200:
|
|
mapping = NoirMapping{"OP200_NOP", ZZZZZZ, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 201:
|
|
mapping = NoirMapping{"OP201_NOP", ZZZZZZ, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 202:
|
|
mapping = NoirMapping{"WALKINGACTOR", WALKINGACTOR, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 203:
|
|
mapping = NoirMapping{"WALKPOLY", WALKPOLY, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 204:
|
|
mapping = NoirMapping{"WALKTAG", WALKTAG, 2};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X, 0x%08X)", mapping.name, pp[0], pp[1]);
|
|
break;
|
|
case 205:
|
|
mapping = NoirMapping{"WALKXPOS", WALKXPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 206:
|
|
mapping = NoirMapping{"WALKYPOS", WALKYPOS, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 207:
|
|
mapping = NoirMapping{"WHICHCD", WHICHCD, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 208:
|
|
mapping = NoirMapping{"WHICHINVENTORY", WHICHINVENTORY, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 209:
|
|
mapping = NoirMapping{"ZZZZZZ", ZZZZZZ, 1};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 210: // STUBBED
|
|
mapping = NoirMapping{"NTBPOLYENTRY", NTBPOLYENTRY, 8};
|
|
pp -= mapping.numArgs - 1;
|
|
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]);
|
|
break;
|
|
case 211: // 4 parameters
|
|
error("Unsupported libCode %d PLAYSEQUENCE", libCode);
|
|
case 212: // STUBBED
|
|
mapping = NoirMapping{"NTBPOLYPREVPAGE", NTBPOLYPREVPAGE, 8};
|
|
pp -= mapping.numArgs - 1;
|
|
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]);
|
|
break;
|
|
case 213: // STUBBED
|
|
mapping = NoirMapping{"NTBPOLYNEXTPAGE", NTBPOLYNEXTPAGE, 8};
|
|
pp -= mapping.numArgs - 1;
|
|
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]);
|
|
break;
|
|
case 214:
|
|
mapping = NoirMapping{"SET3DTEXTURE", SET3DTEXTURE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 215: // this case produces "Unsupported library function" error,
|
|
// followed by "You cant possibly get here", so map it to an invalid value
|
|
mapping = NoirMapping{"OP215_USUPPORTED", -1, 0};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 216: // 2 parameters
|
|
mapping = NoirMapping{"OP216_NOP", ZZZZZZ, 2};
|
|
debug(7, "%s()", mapping.name);
|
|
break;
|
|
case 217:
|
|
mapping = NoirMapping{"VOICEOVER", VOICEOVER, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 224: // 1 parameter, child function reads model data
|
|
error("Unsupported libCode %d", libCode);
|
|
case 111: // no hold frame
|
|
case 225: // hold frame
|
|
mapping = NoirMapping{"PLAYMOVIE", PLAYMOVIE, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 226: // 1 parameter, waits until arg matches an spr related global
|
|
error("Unsupported libCode %d", libCode);
|
|
case 227: // 1 parameter, removes icon
|
|
error("Unsupported libCode %d remoce_conversation_icon", libCode);
|
|
case 228: // STUBBED, PCM related
|
|
warning("TODO: Implement OP228, PCM related");
|
|
mapping = NoirMapping{"OP228", ZZZZZZ, 1};
|
|
pp -= mapping.numArgs - 1;
|
|
debug(7, "%s(0x%08X)", mapping.name, pp[0]);
|
|
break;
|
|
case 229: // 0 parameters
|
|
error("Unsupported libCode %d close_notebook", libCode);
|
|
case 230: // 1 parameter, sets multiobject anim and z
|
|
error("Unsupported libCode %d", libCode);
|
|
default:
|
|
error("Unmapped libCode %d", libCode);
|
|
}
|
|
|
|
return mapping;
|
|
}
|
|
|
|
|
|
/**
|
|
* Subtract one less that the number of parameters from pp
|
|
* pp then points to the first parameter.
|
|
*
|
|
* If the library function has no return value:
|
|
* return -(the number of parameters) to pop them from the stack
|
|
*
|
|
* If the library function has a return value:
|
|
* return -(the number of parameters - 1) to pop most of them from
|
|
* the stack, and stick the return value in pp[0]
|
|
* @param operand Library function
|
|
* @param pp Top of parameter stack
|
|
*/
|
|
int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState) {
|
|
int libCode;
|
|
if (TinselVersion == 0) libCode = DW1DEMO_CODES[operand];
|
|
else if (TinselVersion == 1) libCode = DW1_CODES[operand];
|
|
else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand];
|
|
else if (TinselVersion == 3) {
|
|
NoirMapping mapping = translateNoirLibCode(operand, pp);
|
|
libCode = mapping.libCode;
|
|
if (libCode == ZZZZZZ) {
|
|
debug(7, "%08X CallLibraryRoutine op %d (escOn %d, myEscape %d)", pic->hCode, operand, pic->escOn, pic->myEscape);
|
|
return -mapping.numArgs;
|
|
}
|
|
}
|
|
else libCode = DW2_CODES[operand];
|
|
|
|
debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
|
|
switch (libCode) {
|
|
case ACTORATTR:
|
|
// DW1 only
|
|
pp -= 3; // 4 parameters
|
|
ActorAttr(pp[0], pp[1], pp[2], pp[3]);
|
|
return -4;
|
|
|
|
case ACTORBRIGHTNESS:
|
|
// DW2 only
|
|
pp -= 1;
|
|
ActorBrightness(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case ACTORDIRECTION:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = ActorDirection(pp[0]);
|
|
return 0;
|
|
|
|
case ACTORPALETTE:
|
|
// DW2 only
|
|
pp -= 2; // 3 parameters
|
|
ActorPalette(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case ACTORPRIORITY:
|
|
// DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
ActorPriority(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case ACTORREF:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion != 0)
|
|
error("actorref isn't a real function");
|
|
return 0;
|
|
|
|
case ACTORRGB:
|
|
// Common to DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
ActorRGB(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case ACTORSCALE:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = ActorScale(pp[0]);
|
|
return 0;
|
|
|
|
case ACTORSON:
|
|
// DW1 only
|
|
ActorsOn();
|
|
return 0;
|
|
|
|
case ACTORXPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = ActorPos(ACTORXPOS, pp[0]);
|
|
return 0;
|
|
|
|
case ACTORYPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = ActorPos(ACTORYPOS, pp[0]);
|
|
return 0;
|
|
|
|
case ADDCONV:
|
|
// Noir only
|
|
warning("TODO: Implement ADDCONV");
|
|
return -1;
|
|
|
|
case ADDHIGHLIGHT:
|
|
// DW2 only
|
|
// Command doesn't actually do anything
|
|
pp -= 1; // 2 parameters
|
|
return -2;
|
|
|
|
case ADDINV:
|
|
// DW2 only
|
|
AddInv(INV_DEFAULT, pp[0]);
|
|
return -1;
|
|
|
|
case ADDINV1:
|
|
// Common to DW1 / DW2 / Noir
|
|
AddInv(INV_1, pp[0]);
|
|
return -1;
|
|
|
|
case ADDINV2:
|
|
// Common to both DW1 & DW2
|
|
AddInv(INV_2, pp[0]);
|
|
return -1;
|
|
|
|
case ADDINV3:
|
|
// Noir only
|
|
AddInv(INV_3, pp[0]);
|
|
return -1;
|
|
|
|
case ADDNOTEBOOK:
|
|
// Noir Only
|
|
_vm->_notebook->addClue(pp[0]);
|
|
return -1;
|
|
|
|
case ADDOPENINV:
|
|
// Common to both DW1 & DW2
|
|
AddInv((TinselVersion >= 2) ? DW2_INV_OPEN : INV_OPEN, pp[0]);
|
|
return -1;
|
|
|
|
case ADDTOPIC:
|
|
// Common to both DW1 & DW2
|
|
AddTopic(pp[0]);
|
|
return -1;
|
|
|
|
case AUXSCALE:
|
|
// DW1 only
|
|
pp -= 13; // 14 parameters
|
|
AuxScale(pp[0], pp[1], (SCNHANDLE *)(pp+2));
|
|
return -14;
|
|
|
|
case BACKGROUND:
|
|
// Common to DW1 / DW2 / Noir
|
|
startBackground(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case BOOKADDHYPERLINK:
|
|
// Noir
|
|
pp -= 1; // 2 parameters
|
|
_vm->_notebook->addHyperlink(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case BLOCKING:
|
|
// DW2 only
|
|
Blocking(pp[0]);
|
|
return -1;
|
|
|
|
case CALLACTOR:
|
|
case CALLGLOBALPROCESS:
|
|
case CALLOBJECT:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
bool result;
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic, &result);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
pp[0] = result ? 1 : 0;
|
|
} else {
|
|
uint32 v;
|
|
if (libCode == CALLACTOR)
|
|
v = SendActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
|
|
else if (libCode == CALLGLOBALPROCESS)
|
|
v = SendGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
else
|
|
v = SendObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
|
|
if (coroParam)
|
|
return 0;
|
|
pp[0] = v;
|
|
}
|
|
|
|
if (!pp[0])
|
|
KillSelf(coroParam);
|
|
return -2;
|
|
|
|
|
|
case CALLPROCESS:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
bool result;
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic, &result);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
|
|
pp[0] = result ? 1 : 0;
|
|
} else {
|
|
int result = SendProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
if (coroParam)
|
|
return 0;
|
|
|
|
pp[0] = result;
|
|
}
|
|
return -2;
|
|
|
|
case CALLSCENE:
|
|
// DW2 only
|
|
error("CallScene isn't a real function");
|
|
|
|
case CALLTAG:
|
|
// DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
bool result;
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic, &result);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
|
|
pp[0] = result ? 1 : 0;
|
|
} else {
|
|
bool result;
|
|
SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
|
|
if (coroParam)
|
|
return 0;
|
|
|
|
pp[0] = result ? 1 : 0;
|
|
}
|
|
|
|
if (!pp[0])
|
|
KillSelf(coroParam);
|
|
return -2;
|
|
|
|
case CAMERA:
|
|
// Common to both DW1 & DW2
|
|
Camera(pp[0]);
|
|
return -1;
|
|
|
|
case CDCHANGESCENE:
|
|
// DW2 / Noir
|
|
CdChangeScene(pp[0]);
|
|
return -1;
|
|
|
|
case CDDOCHANGE:
|
|
// DW2 only
|
|
CdDoChange(coroParam);
|
|
return 0;
|
|
|
|
case CDENDACTOR:
|
|
// DW2 only
|
|
CdEndActor(pp[0], pic->myEscape);
|
|
return -1;
|
|
|
|
case CDLOAD:
|
|
// Common to both DW1 & DW2
|
|
pp -= 1; // 2 parameters
|
|
CDload(pp[0], pp[1], pic->myEscape);
|
|
return -2;
|
|
|
|
case CDPLAY:
|
|
// Common to both DW1 & DW2
|
|
error("cdplay isn't a real function");
|
|
|
|
case CLEARHOOKSCENE:
|
|
// Common to both DW1 & DW2
|
|
ClearHookScene();
|
|
return 0;
|
|
|
|
case CLOSEINVENTORY:
|
|
// Common to both DW1 & DW2
|
|
CloseInventory();
|
|
return 0;
|
|
|
|
case CONTROL:
|
|
// Common to DW1 / DW2 / Noir
|
|
Control(pp[0]);
|
|
return -1;
|
|
|
|
case CONVERSATION:
|
|
// Common to both DW1 & DW2
|
|
Conversation(coroParam, pp[0], pic->hPoly, pic->idActor, pic->escOn, pic->myEscape);
|
|
return -1;
|
|
|
|
case CONVTOPIC:
|
|
// Common to both DW1 & DW2
|
|
ConvTopic(pp[0]);
|
|
return -1;
|
|
|
|
case CROSSCLUE:
|
|
// Noir only
|
|
_vm->_notebook->crossClue(pp[0]);
|
|
return -1;
|
|
|
|
case CURSOR:
|
|
// DW2 only
|
|
ToggleCursor(pp[0]);
|
|
return -1;
|
|
|
|
case CURSORXPOS:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp[0] = CursorPos(CURSORXPOS);
|
|
return 0;
|
|
|
|
case CURSORYPOS:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp[0] = CursorPos(CURSORYPOS);
|
|
return 0;
|
|
|
|
case CUTSCENE:
|
|
// DW1 only
|
|
error("cutscene isn't a real function");
|
|
|
|
case DEC3D:
|
|
// Noir only
|
|
pp -= 2;
|
|
Dec3D(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case DECCONVW:
|
|
// Common to both DW1 & DW2
|
|
pp -= 7; // 8 parameters
|
|
DecConvW(pp[0], pp[1], pp[2], pp[3],
|
|
pp[4], pp[5], pp[6], pp[7]);
|
|
return -8;
|
|
|
|
case DECCSTRINGS:
|
|
// DW1 only
|
|
pp -= 19; // 20 parameters
|
|
DecCStrings((SCNHANDLE *)pp);
|
|
return -20;
|
|
|
|
case DECCURSOR:
|
|
// Common to both DW1 & DW2
|
|
DecCursor(pp[0]);
|
|
return -1;
|
|
|
|
case DECFLAGS:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2)
|
|
error("DecFlags() is obsolete");
|
|
|
|
DecFlags(pp[0]);
|
|
return -1;
|
|
|
|
case DECINV1:
|
|
// Common to both DW1 & DW2
|
|
pp -= 7; // 8 parameters
|
|
DecInv1(pp[0], pp[1], pp[2], pp[3],
|
|
pp[4], pp[5], pp[6], pp[7]);
|
|
return -8;
|
|
|
|
case DECINV2:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 7; // 8 parameters
|
|
DecInv2(pp[0], pp[1], pp[2], pp[3],
|
|
pp[4], pp[5], pp[6], pp[7]);
|
|
return -8;
|
|
|
|
case DECINVMAIN:
|
|
pp -= 7; // 8 parameters
|
|
DecInvMain(pp[0], pp[1], pp[2], pp[3],
|
|
pp[4], pp[5], pp[6], pp[7]);
|
|
return -8;
|
|
|
|
case DECINVW:
|
|
// Common to both DW1 & DW2
|
|
DecInvW(pp[0]);
|
|
return -1;
|
|
|
|
case DECLARELANGUAGE:
|
|
// Common to DW2 & Noir
|
|
pp -= 2; // 3 parameters
|
|
DeclareLanguage(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case DECLEAD:
|
|
// Common to DW1 / DW2 / Noir
|
|
if (TinselVersion >= 2) {
|
|
DecLead(pp[0]);
|
|
return -1;
|
|
} else {
|
|
pp -= 61; // 62 parameters
|
|
DecLead(pp[0], (SCNHANDLE *)&pp[1], pp[61]);
|
|
return -62;
|
|
}
|
|
|
|
case DECSCALE:
|
|
// DW2 only
|
|
pp -= 13; // 14 parameters
|
|
DecScale(pp[0], pp[1], pp[2], pp[3], pp[4],
|
|
pp[5], pp[6], pp[7], pp[8], pp[9],
|
|
pp[10], pp[11], pp[12], pp[13]);
|
|
return -14;
|
|
|
|
case DECTAGFONT:
|
|
// Common to DW1 / DW2 / Noir
|
|
_vm->_font->SetTagFontHandle(pp[0]);
|
|
return -1;
|
|
|
|
case DECTALKFONT:
|
|
// Common to DW1 / DW2 / Noir
|
|
_vm->_font->SetTalkFontHandle(pp[0]);
|
|
return -1;
|
|
|
|
case DELICON:
|
|
// DW1 only
|
|
DelIcon(pp[0]);
|
|
return -1;
|
|
|
|
case DELINV:
|
|
// DW1 only
|
|
DelInv(pp[0]);
|
|
return -1;
|
|
|
|
case DELTOPIC:
|
|
// DW2 only
|
|
DelTopic(pp[0]);
|
|
return -1;
|
|
|
|
case DIMMUSIC:
|
|
// DW2 only
|
|
_vm->_pcmMusic->dim(true);
|
|
return 0;
|
|
|
|
case DROP:
|
|
// DW2 only
|
|
Drop(pp[0]);
|
|
return -1;
|
|
|
|
case DROPEVERYTHING:
|
|
// DW2 only
|
|
DropEverything();
|
|
return 0;
|
|
|
|
case DROPOUT:
|
|
// DW1 only
|
|
error("DropOut (%d)", pp[0]);
|
|
|
|
case EFFECTACTOR:
|
|
// Common to both DW1 & DW2
|
|
assert(pic->event == WALKIN || pic->event == WALKOUT); // effectactor() must be from effect poly code
|
|
|
|
pp[0] = pic->idActor;
|
|
return 0;
|
|
|
|
case ENABLEMENU:
|
|
// Common to both DW1 & DW2
|
|
EnableMenu();
|
|
return 0;
|
|
|
|
case ENDACTOR:
|
|
// DW2 & Noir
|
|
EndActor(pp[0]);
|
|
return -1;
|
|
|
|
case ESCAPE:
|
|
case ESCAPEOFF:
|
|
case ESCAPEON:
|
|
// Common to both DW1 & DW2
|
|
error("Escape isn't a real function");
|
|
|
|
case EVENT:
|
|
// Common to DW1 / DW2 / Noir
|
|
if (TinselVersion >= 2)
|
|
pp[0] = pic->event;
|
|
else
|
|
pp[0] = TINSEL1_EVENT_MAP[pic->event];
|
|
return 0;
|
|
|
|
case FACETAG:
|
|
// DW2 only
|
|
FaceTag(pp[0], pic->hPoly);
|
|
return -1;
|
|
|
|
case FADEIN:
|
|
// DW2 only
|
|
FadeInMedium();
|
|
return 0;
|
|
|
|
case FADEMIDI:
|
|
// DW1 only
|
|
FadeMidi(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case FADEMUSIC:
|
|
// Noir only
|
|
pp -= 1;
|
|
FadeMusic(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case FADEOUT:
|
|
// DW1 only
|
|
FadeOutMedium();
|
|
return 0;
|
|
|
|
case FRAMEGRAB:
|
|
// Common to both DW1 & DW2
|
|
return -1;
|
|
|
|
case FREEZECURSOR:
|
|
// DW2 only
|
|
FreezeCursor(pp[0]);
|
|
return -1;
|
|
|
|
case GETINVLIMIT:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = GetInvLimit(pp[0]);
|
|
return 0;
|
|
|
|
case GHOST:
|
|
// DW2 only
|
|
pp -= 2; // 3 parameters
|
|
Ghost(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case GLOBALVAR:
|
|
// DW1 only
|
|
error("GlobalVar isn't a real function");
|
|
|
|
case GRABMOVIE:
|
|
// DW2 only
|
|
return -1;
|
|
|
|
case HAILSCENE:
|
|
// DW2 only
|
|
HailScene(pp[0]);
|
|
return -1;
|
|
|
|
case HASRESTARTED:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = HasRestarted();
|
|
return 0;
|
|
|
|
case HAVE:
|
|
// DW2 / Noir
|
|
pp[0] = Have(pp[0]);
|
|
return 0; // using return value
|
|
|
|
case HELDOBJECT:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = HeldObject();
|
|
return 0;
|
|
|
|
case HELDOBJECTORTOPIC:
|
|
// Noir
|
|
if (_vm->_dialogs->isConvAndNotMove()) {
|
|
pp[0] = HeldObject();
|
|
} else {
|
|
pp[0] = Topic();
|
|
}
|
|
return 0;
|
|
|
|
case HIDEACTOR:
|
|
// Common to DW1 / DW2 / Noir
|
|
if (TinselVersion <= 1)
|
|
HideActorFn(coroParam, pp[0]);
|
|
else if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
} else
|
|
HideActorFn(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case HIDEBLOCK:
|
|
// DW2 only
|
|
HideBlock(pp[0]);
|
|
return -1;
|
|
|
|
case HIDEEFFECT:
|
|
// DW2 only
|
|
HideEffect(pp[0]);
|
|
return -1;
|
|
|
|
case HIDEPATH:
|
|
// DW2 only
|
|
HidePath(pp[0]);
|
|
return -1;
|
|
|
|
case HIDEREFER:
|
|
// DW2 / Noir
|
|
HideRefer(pp[0]);
|
|
return -1;
|
|
|
|
case HIDETAG:
|
|
// DW2 only
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
} else {
|
|
HideTag(coroParam, pp[0], pic->hPoly);
|
|
if (coroParam)
|
|
return 0;
|
|
}
|
|
return -1;
|
|
|
|
case HOLD:
|
|
// DW2 only
|
|
Hold(pp[0]);
|
|
return -1;
|
|
|
|
case HOOKSCENE:
|
|
// Common to both DW1 & DW2
|
|
pp -= 2; // 3 parameters
|
|
HookScene(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case IDLETIME:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = IdleTime();
|
|
return 0;
|
|
|
|
case ININVENTORY:
|
|
// DW1 only
|
|
pp[0] = InInventory(pp[0]);
|
|
return 0; // using return value
|
|
|
|
case INSTANTSCROLL:
|
|
// DW2 only
|
|
InstantScroll(pp[0]);
|
|
return -1;
|
|
|
|
case INVDEPICT:
|
|
// DW1 only
|
|
pp -= 1; // 2 parameters
|
|
InvDepict(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case INVENTORY:
|
|
// Common to both DW1 & DW2
|
|
Inventory(pp[0], pic->escOn, pic->myEscape);
|
|
return -1;
|
|
|
|
case INVPLAY:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
InvPlay(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case INWHICHINV:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = InWhichInv(pp[0]);
|
|
return 0; // using return value
|
|
|
|
case KILLACTOR:
|
|
// DW1 only
|
|
if (TinselVersion >= 2)
|
|
error("KillActor() was not expected to be required");
|
|
|
|
KillActor(pp[0]);
|
|
return -1;
|
|
|
|
case KILLBLOCK:
|
|
// DW1 only
|
|
KillBlock(pp[0]);
|
|
return -1;
|
|
|
|
case KILLEXIT:
|
|
// DW1 only
|
|
KillExit(pp[0]);
|
|
return -1;
|
|
|
|
case KILLGLOBALPROCESS:
|
|
// DW2 only
|
|
KillGlobalProcess(pp[0]);
|
|
return -1;
|
|
|
|
case KILLPROCESS:
|
|
// DW2 / Noir
|
|
KillProcess(pp[0]);
|
|
return -1;
|
|
|
|
case KILLTAG:
|
|
// DW1 only
|
|
KillTag(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case LOCALVAR:
|
|
// DW2 only
|
|
error("LocalVar isn't a real function");
|
|
|
|
case MOVECURSOR:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
MoveCursor(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case MOVETAG:
|
|
// DW2 only
|
|
pp -= 2; // 3 parameters
|
|
MoveTag(pp[0], pp[1], pp[2], pic->hPoly);
|
|
return -3;
|
|
|
|
case MOVETAGTO:
|
|
// DW2 only
|
|
pp -= 2; // 3 parameters
|
|
MoveTagTo(pp[0], pp[1], pp[2], pic->hPoly);
|
|
return -3;
|
|
|
|
case NEWSCENE:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 2; // 3 parameters
|
|
if (*pResumeState == RES_2)
|
|
*pResumeState = RES_NOT;
|
|
else
|
|
NewScene(coroParam, pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case NOBLOCKING:
|
|
// Common to both DW1 & DW2
|
|
NoBlocking();
|
|
return 0;
|
|
|
|
case NOPAUSE:
|
|
// DW2 only
|
|
g_bNoPause = true;
|
|
return 0;
|
|
|
|
case NTBPOLYENTRY:
|
|
// Noir only
|
|
pp -= 7; // 8 Parameters
|
|
NotebookPolyEntry(Common::Point(pp[0], pp[1]),
|
|
Common::Point(pp[2], pp[3]),
|
|
Common::Point(pp[4], pp[5]),
|
|
Common::Point(pp[6], pp[7]));
|
|
return -8;
|
|
|
|
case NTBPOLYNEXTPAGE:
|
|
// Noir only
|
|
pp -= 7; // 8 Parameters
|
|
NotebookPolyNextPage(Common::Point(pp[0], pp[1]),
|
|
Common::Point(pp[2], pp[3]),
|
|
Common::Point(pp[4], pp[5]),
|
|
Common::Point(pp[6], pp[7]));
|
|
return -8;
|
|
|
|
case NTBPOLYPREVPAGE:
|
|
// Noir only
|
|
pp -= 7; // 8 Parameters
|
|
NotebookPolyPrevPage(Common::Point(pp[0], pp[1]),
|
|
Common::Point(pp[2], pp[3]),
|
|
Common::Point(pp[4], pp[5]),
|
|
Common::Point(pp[6], pp[7]));
|
|
return -8;
|
|
|
|
case NOSCROLL:
|
|
// Common to both DW1 & DW2
|
|
pp -= 3; // 4 parameters
|
|
NoScroll(pp[0], pp[1], pp[2], pp[3]);
|
|
return -4;
|
|
|
|
case OBJECTHELD:
|
|
// DW1 only
|
|
ObjectHeld(pp[0]);
|
|
return -1;
|
|
|
|
case OFFSET:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 2; // 2 parameters
|
|
Offset((EXTREME)pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
} else {
|
|
pp -= 1; // 2 parameters
|
|
Offset(EX_USEXY, pp[0], pp[1]);
|
|
return -2;
|
|
}
|
|
|
|
case OTHEROBJECT:
|
|
// DW2 only
|
|
pp[0] = OtherObject(pic->pinvo);
|
|
return 0;
|
|
|
|
case OPENNOTEBOOK:
|
|
// Noir only
|
|
_vm->_notebook->show(0);
|
|
return 0;
|
|
|
|
case PAUSE:
|
|
// DW2 only
|
|
WaitTime(coroParam, 1, true, pic->escOn, pic->myEscape);
|
|
return 0;
|
|
|
|
case PLAY:
|
|
// Common to DW1 / DW2 / Noir
|
|
if (TinselVersion == 3) {
|
|
if (*pResumeState == RES_1 && _vm->_handle->IsCdPlayHandle(pp[0])) {
|
|
*pResumeState = RES_NOT;
|
|
if ((pp[0] & 0x10) != 0) {
|
|
return -4;
|
|
}
|
|
return -2;
|
|
} else if ((pp[0] & 0x10) != 0) {
|
|
Play(coroParam, pp[-1], pp[-3], pp[-2], pp[0], pic->myEscape, false, pic->event, pic->hPoly, pic->idActor);
|
|
return -4;
|
|
}
|
|
Play(coroParam, pp[-1], -1, -1, pp[0], pic->myEscape, false, pic->event, pic->hPoly, pic->idActor);
|
|
return -2;
|
|
|
|
} if (TinselVersion >= 2) {
|
|
pp -= 3; // 4 parameters
|
|
if (*pResumeState == RES_1 && _vm->_handle->IsCdPlayHandle(pp[0]))
|
|
*pResumeState = RES_NOT;
|
|
else {
|
|
Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false, pic->event, pic->hPoly, pic->idActor);
|
|
}
|
|
return -4;
|
|
|
|
} else {
|
|
pp -= 5; // 6 parameters
|
|
|
|
if (pic->event == WALKIN || pic->event == WALKOUT)
|
|
Play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myEscape, false);
|
|
else
|
|
Play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape, false);
|
|
return -6;
|
|
}
|
|
|
|
case PLAYMIDI:
|
|
// Common to both DW1 & DW2
|
|
pp -= 2; // 3 parameters
|
|
PlayMidi(coroParam, pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case PLAYMOVIE:
|
|
if (TinselVersion == 3) {
|
|
t3PlayMovie(coroParam, pp[0], pic->myEscape);
|
|
} else {
|
|
// DW2 only
|
|
PlayMovie(coroParam, pp[0], pic->myEscape);
|
|
}
|
|
return -1;
|
|
|
|
case PLAYMUSIC:
|
|
// DW2 / Noir only
|
|
PlayMusic(pp[0]);
|
|
return -1;
|
|
|
|
case PLAYRTF:
|
|
// Common to both DW1 & DW2
|
|
error("playrtf only applies to cdi");
|
|
|
|
case PLAYSAMPLE:
|
|
// Common to DW1 / DW2 / Noir
|
|
if (TinselVersion >= 2) {
|
|
pp -= 3; // 4 parameters
|
|
PlaySample(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape);
|
|
return -4;
|
|
} else {
|
|
pp -= 1; // 2 parameters
|
|
PlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
|
|
return -2;
|
|
}
|
|
|
|
case POINTACTOR:
|
|
// DW2 only
|
|
PointActor(pp[0]);
|
|
return -1;
|
|
|
|
case POINTTAG:
|
|
// DW2 only
|
|
PointTag(pp[0], pic->hPoly);
|
|
return -1;
|
|
|
|
case POSTACTOR:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
PostActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->idActor, pic->myEscape);
|
|
return -2;
|
|
|
|
case POSTGLOBALPROCESS:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
PostGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
return -2;
|
|
|
|
case POSTOBJECT:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
PostObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
return -2;
|
|
|
|
case POSTPROCESS:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
PostProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
|
|
return -2;
|
|
|
|
case POSTTAG:
|
|
// DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
PostTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
|
|
return -2;
|
|
|
|
|
|
case PREPARESCENE:
|
|
// DW1 only
|
|
PrepareScene(pp[0]);
|
|
return -1;
|
|
|
|
case PRINT:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 4; // 5 parameters
|
|
Print(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4] != 0, pic->escOn, pic->myEscape);
|
|
return -5;
|
|
} else {
|
|
pp -= 5; // 6 parameters
|
|
/* pp[2] was intended to be attribute */
|
|
Print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5] == 2, pic->escOn, pic->myEscape);
|
|
return -6;
|
|
}
|
|
|
|
case PRINTCURSOR:
|
|
// DW2 / Noir only
|
|
PrintTag(pic->hPoly, pp[0], pic->idActor, true);
|
|
return -1;
|
|
|
|
case PRINTOBJ:
|
|
// Common to both DW1 & DW2
|
|
PrintObj(coroParam, pp[0], pic->pinvo, pic->event, pic->myEscape);
|
|
return -1;
|
|
|
|
case PRINTTAG:
|
|
// Common to DW1 / DW2 / Noir
|
|
PrintTag(pic->hPoly, pp[0], (TinselVersion >= 2) ? pic->idActor : 0, false);
|
|
return -1;
|
|
|
|
case QUITGAME:
|
|
// Common to both DW1 & DW2
|
|
QuitGame();
|
|
return 0;
|
|
|
|
case RANDOM:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 2; // 3 parameters
|
|
pp[0] = RandomFn(pp[0], pp[1], pp[2]);
|
|
return -2; // One holds return value
|
|
|
|
case RESETIDLETIME:
|
|
// Common to both DW1 & DW2
|
|
ResetIdleTime();
|
|
return 0;
|
|
|
|
case RESTARTGAME:
|
|
// Common to both DW1 & DW2
|
|
FnRestartGame();
|
|
return 0;
|
|
|
|
case RESTORESCENE:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
RestoreScene(coroParam, (TRANSITS)pp[0]);
|
|
return -1;
|
|
} else {
|
|
RestoreScene(coroParam, TRANS_FADE);
|
|
return 0;
|
|
}
|
|
|
|
case RESTORE_CUT:
|
|
// DW1 only
|
|
RestoreScene(coroParam, TRANS_CUT);
|
|
return 0;
|
|
|
|
case RESUMELASTGAME:
|
|
// DW2 only
|
|
ResumeLastGame();
|
|
return 0;
|
|
|
|
case RUNMODE:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = RunMode();
|
|
return 0;
|
|
|
|
case SAMPLEPLAYING:
|
|
// DW1 only
|
|
pp[0] = SamplePlaying(pic->escOn, pic->myEscape);
|
|
return 0;
|
|
|
|
case SAVESCENE:
|
|
// Common to both DW1 & DW2
|
|
if (*pResumeState == RES_1)
|
|
*pResumeState = RES_2;
|
|
else
|
|
SaveScene(coroParam);
|
|
return 0;
|
|
|
|
case SAY:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
TalkOrSay(coroParam, IS_SAY, pp[1], 0, 0, 0, pp[0], false, pic->escOn, pic->myEscape);
|
|
return -2;
|
|
|
|
case SAYAT:
|
|
// DW2 only
|
|
pp -= 4; // 5 parameters
|
|
TalkOrSay(coroParam, IS_SAYAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
|
|
return -5;
|
|
|
|
case VOICEOVER:
|
|
// Noir only
|
|
TalkOrSay(coroParam, IS_SAY, pp[0], 0, 0, 0, SystemVar(SV_USER2), false, pic->escOn, pic->myEscape);
|
|
return -1;
|
|
|
|
case SCALINGREELS:
|
|
// Common to both DW1 & DW2
|
|
pp -= 6; // 7 parameters
|
|
ScalingReels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
|
|
return -7;
|
|
|
|
case SCANICON:
|
|
// DW1 only
|
|
pp[0] = ScanIcon();
|
|
return 0;
|
|
|
|
case SCREENXPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = LToffset(SCREENXPOS);
|
|
return 0;
|
|
|
|
case SCREENYPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = LToffset(SCREENYPOS);
|
|
return 0;
|
|
|
|
case SCROLL:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 5; // 6 parameters
|
|
ScrollScreen(coroParam, (EXTREME)pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pic->escOn, pic->myEscape);
|
|
return -6;
|
|
} else {
|
|
pp -= 3; // 4 parameters
|
|
ScrollScreen(coroParam, EX_USEXY, pp[0], pp[1], pp[2], pp[2], pp[3], pic->escOn, pic->myEscape);
|
|
return -4;
|
|
}
|
|
|
|
case SCROLLPARAMETERS:
|
|
// DW2 only
|
|
pp -= 6; // 7 parameters
|
|
ScrollParameters(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
|
|
return -7;
|
|
|
|
case SENDTAG:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
bool result;
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic, &result);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
pp[0] = result ? 1 : 0;
|
|
} else {
|
|
bool result;
|
|
SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
|
|
if (coroParam)
|
|
return 0;
|
|
|
|
pp[0] = result;
|
|
}
|
|
return -1;
|
|
|
|
case SET3DTEXTURE:
|
|
// Noir only
|
|
warning("TODO: Implement SET3DTEXTURE(0x%08X)", pp[0]);
|
|
return -1;
|
|
|
|
case SETACTOR:
|
|
// DW1 only
|
|
SetActor(pp[0]);
|
|
return -1;
|
|
|
|
case SETBLOCK:
|
|
// DW1 only
|
|
SetBlock(pp[0]);
|
|
return -1;
|
|
|
|
case SETEXIT:
|
|
// DW1 only
|
|
SetExit(pp[0]);
|
|
return -1;
|
|
|
|
case SETINVLIMIT:
|
|
// Common to both DW1 & DW2
|
|
pp -= 1; // 2 parameters
|
|
SetInvLimit(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case SETINVSIZE:
|
|
// Common to both DW1 & DW2
|
|
pp -= 6; // 7 parameters
|
|
SetInvSize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
|
|
return -7;
|
|
|
|
case SETLANGUAGE:
|
|
// Common to both DW1 & DW2
|
|
SetLanguage((LANGUAGE)pp[0]);
|
|
return -1;
|
|
|
|
case SETPALETTE:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
// Note: Although DW2 introduces parameters for start and length, it doesn't use them
|
|
pp -= 2;
|
|
SetPalette(pp[0], pic->escOn, pic->myEscape);
|
|
return -3;
|
|
} else {
|
|
SetPalette(pp[0], pic->escOn, pic->myEscape);
|
|
return -1;
|
|
}
|
|
|
|
case SETSYSTEMREEL:
|
|
// Noir only
|
|
if (TinselVersion == 3) {
|
|
pp -= 1;
|
|
SetSystemReel(pp[0], pp[1]);
|
|
return -2;
|
|
} else {
|
|
error("SETSYSTEMREEL is only used in Noir");
|
|
}
|
|
|
|
case SETSYSTEMSTRING:
|
|
// DW2 & Noir
|
|
pp -= 1; // 2 parameters
|
|
SetSystemString(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case SETSYSTEMVAR:
|
|
// DW1 & Noir
|
|
pp -= 1; // 2 parameters
|
|
SetSystemVar(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case SETTAG:
|
|
// DW1 only
|
|
SetTag(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case SETTIMER:
|
|
// DW1 only
|
|
pp -= 3; // 4 parameters
|
|
SetTimer(pp[0], pp[1], pp[2], pp[3]);
|
|
return -4;
|
|
|
|
case SETVIEW:
|
|
// Noir only
|
|
pp -= 1;
|
|
SetView(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case SHELL:
|
|
// DW2 only
|
|
Shell(pp[0]);
|
|
return 0;
|
|
|
|
case SHOWACTOR:
|
|
// DW2 & Noir
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic);
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
} else
|
|
ShowActorFn(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case SHOWBLOCK:
|
|
// DW2 only
|
|
ShowBlock(pp[0]);
|
|
return -1;
|
|
|
|
case SHOWEFFECT:
|
|
// DW2 only
|
|
ShowEffect(pp[0]);
|
|
return -1;
|
|
|
|
case SHOWMENU:
|
|
// DW2 / Noir
|
|
ShowMenu();
|
|
return 0;
|
|
|
|
case SHOWPATH:
|
|
// DW2 only
|
|
ShowPath(pp[0]);
|
|
return -1;
|
|
|
|
case SHOWPOS:
|
|
// DW1 only
|
|
#ifdef DEBUG
|
|
showpos();
|
|
#endif
|
|
return 0;
|
|
|
|
case SHOWREFER:
|
|
// DW2 only
|
|
ShowRefer(pp[0]);
|
|
return -1;
|
|
|
|
case SHOWSTRING:
|
|
#ifdef DEBUG
|
|
showstring();
|
|
#endif
|
|
return 0;
|
|
|
|
case SHOWTAG:
|
|
// DW2 / Noir
|
|
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
|
|
*pResumeState = RES_NOT;
|
|
FinishWaiting(coroParam, pic);
|
|
|
|
if (coroParam) {
|
|
*pResumeState = RES_1;
|
|
return 0;
|
|
}
|
|
} else {
|
|
ShowTag(coroParam, pp[0], pic->hPoly);
|
|
}
|
|
return -1;
|
|
|
|
case SPLAY:
|
|
// DW1 only
|
|
pp -= 6; // 7 parameters
|
|
|
|
if (pic->event == WALKIN || pic->event == WALKOUT)
|
|
SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myEscape);
|
|
else
|
|
SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], pic->idActor, pic->escOn, pic->myEscape);
|
|
return -7;
|
|
|
|
case STAND:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 3; // 4 parameters
|
|
Stand(coroParam, pp[0], pp[1], pp[2], pp[3]);
|
|
return -4;
|
|
|
|
case STANDTAG:
|
|
// Common to both DW1 & DW2
|
|
StandTag(pp[0], pic->hPoly);
|
|
return -1;
|
|
|
|
case STARTGLOBALPROCESS:
|
|
// DW2 only
|
|
StartGlobalProcess(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case STARTPROCESS:
|
|
// DW2 / Noir
|
|
StartProcess(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case STARTTIMER:
|
|
// DW2 only
|
|
pp -= 3; // 4 parameters
|
|
StartTimerFn(pp[0], pp[1], pp[2], pp[3]);
|
|
return -4;
|
|
|
|
case STOPMIDI:
|
|
// DW1 only
|
|
StopMidiFn();
|
|
return 0;
|
|
|
|
case STOPSAMPLE:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
StopSample(pp[0]);
|
|
return -1;
|
|
} else {
|
|
StopSample();
|
|
return 0;
|
|
}
|
|
|
|
case STOPWALK:
|
|
// Common to both DW1 & DW2 only
|
|
StopWalk(pp[0]);
|
|
return -1;
|
|
|
|
case SUBTITLES:
|
|
// Common to both DW1 & DW2
|
|
Subtitles(pp[0]);
|
|
return -1;
|
|
|
|
case SWALK:
|
|
// Common to both DW1 & DW2
|
|
pp -= 5; // 6 parameters
|
|
Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], -1, pic->escOn, pic->myEscape);
|
|
return -6;
|
|
|
|
case SWALKZ:
|
|
// DW2 only
|
|
pp -= 6; // 7 parameters
|
|
Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pic->escOn, pic->myEscape);
|
|
return -7;
|
|
|
|
case SYSTEMVAR:
|
|
// DW2 / Noir
|
|
pp[0] = SystemVar(pp[0]);
|
|
return 0;
|
|
|
|
case TAGACTOR:
|
|
pp -= 2; // 3 parameters
|
|
TagActor(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case TAGTAGXPOS:
|
|
case TAGTAGYPOS:
|
|
case TAGWALKXPOS:
|
|
case TAGWALKYPOS:
|
|
// DW2 only
|
|
pp[0] = TagPos((MASTER_LIB_CODES)libCode, pp[0], pic->hPoly);
|
|
return 0;
|
|
|
|
case TALK:
|
|
// Common to both DW1 & DW2
|
|
pp -= 1; // 2 parameters
|
|
|
|
if (TinselVersion >= 2)
|
|
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
|
|
else if (pic->event == WALKIN || pic->event == WALKOUT)
|
|
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
|
|
else
|
|
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], pic->idActor, false, pic->escOn, pic->myEscape);
|
|
return -2;
|
|
|
|
case TALKAT:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 4; // 5 parameters
|
|
TalkOrSay(coroParam, IS_TALKAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
|
|
return -5;
|
|
} else {
|
|
pp -= 3; // 4 parameters
|
|
TalkAt(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape);
|
|
return -4;
|
|
}
|
|
|
|
case TALKATS:
|
|
// DW1 only
|
|
pp -= 4; // 5 parameters
|
|
TalkAtS(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pic->escOn, pic->myEscape);
|
|
return -5;
|
|
|
|
case TALKATTR:
|
|
// DW1 only
|
|
pp -= 2; // 3 parameters
|
|
TalkAttr(pp[0], pp[1], pp[2], pic->escOn, pic->myEscape);
|
|
return -3;
|
|
|
|
case TALKPALETTEINDEX:
|
|
// DW1 only
|
|
TalkPaletteIndex(pp[0]);
|
|
return -1;
|
|
|
|
case TALKRGB:
|
|
// DW2 only
|
|
TalkRGB(pp[0], pic->myEscape);
|
|
return -3;
|
|
|
|
case TALKVIA:
|
|
// DW2 / Noir
|
|
TalkVia(pp[0]);
|
|
return -1;
|
|
|
|
case TEMPTAGFONT:
|
|
// DW2 only
|
|
_vm->_font->SetTempTagFontHandle(pp[0]);
|
|
return -1;
|
|
|
|
case TEMPTALKFONT:
|
|
// DW2 only
|
|
_vm->_font->SetTempTalkFontHandle(pp[0]);
|
|
return -1;
|
|
|
|
case THISOBJECT:
|
|
// DW2 only
|
|
pp[0] = ThisObject(pic->pinvo);
|
|
return 0;
|
|
|
|
case THISTAG:
|
|
// DW2 only
|
|
pp[0] = ThisTag(pic->hPoly);
|
|
return 0;
|
|
|
|
case TIMER:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = TimerFn(pp[0]);
|
|
return 0;
|
|
|
|
case TOPIC:
|
|
// DW2 only
|
|
pp[0] = Topic();
|
|
return 0;
|
|
|
|
case TOPPLAY:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 3; // 4 parameters
|
|
TopPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, pic->event);
|
|
return -4;
|
|
} else {
|
|
pp -= 5; // 6 parameters
|
|
TopPlay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape);
|
|
return -6;
|
|
}
|
|
|
|
case TOPWINDOW:
|
|
// Common to both DW1 & DW2
|
|
TopWindow(pp[0]);
|
|
return -1;
|
|
|
|
case TRANSLUCENTINDEX:
|
|
// DW2 only
|
|
TranslucentIndex(pp[0]);
|
|
return -1;
|
|
|
|
case TRYPLAYSAMPLE:
|
|
// DW1 only
|
|
pp -= 1; // 2 parameters
|
|
TryPlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
|
|
return -2;
|
|
|
|
case UNDIMMUSIC:
|
|
// DW2 only
|
|
_vm->_pcmMusic->unDim(true);
|
|
return 0;
|
|
|
|
case UNHOOKSCENE:
|
|
// Common to both DW1 & DW2
|
|
UnHookScene();
|
|
return 0;
|
|
|
|
case UNTAGACTOR:
|
|
// DW1 only
|
|
UnTagActorFn(pp[0]);
|
|
return -1;
|
|
|
|
case VIBRATE:
|
|
// DW1 only
|
|
Vibrate();
|
|
return 0;
|
|
|
|
case WAITFRAME:
|
|
// Common to both DW1 & DW2
|
|
pp -= 1; // 2 parameters
|
|
WaitFrame(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
|
|
return -2;
|
|
|
|
case WAITKEY:
|
|
// Common to both DW1 & DW2
|
|
WaitKey(coroParam, pic->escOn, pic->myEscape);
|
|
return 0;
|
|
|
|
case WAITSCROLL:
|
|
// DW2 only
|
|
WaitScroll(coroParam, pic->myEscape);
|
|
return 0;
|
|
|
|
case WAITTIME:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp -= 1; // 2 parameters
|
|
WaitTime(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
|
|
if (!coroParam && (pic->hCode == 0x3007540) && (pic->resumeState == RES_2))
|
|
// FIXME: This is a hack to return control to the user after using the prunes in
|
|
// the DW1 Demo, since I can't figure out how it's legitimately done
|
|
Control(CONTROL_ON);
|
|
|
|
return -2;
|
|
|
|
case WALK:
|
|
// Common to both DW1 & DW2
|
|
pp -= 4; // 5 parameters
|
|
Walk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], false, -1, pic->escOn, pic->myEscape);
|
|
return -5;
|
|
|
|
case WALKED: {
|
|
// Common to both DW1 & DW2
|
|
pp -= 3; // 4 parameters
|
|
bool tmp = false;
|
|
Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
|
|
if (!coroParam) {
|
|
// Only write the result to the stack if walked actually completed running.
|
|
pp[0] = tmp;
|
|
}
|
|
}
|
|
return -3;
|
|
|
|
case WALKINGACTOR:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
// DW2 doesn't use a second parameter to WalkingActor
|
|
WalkingActor(pp[0]);
|
|
return -1;
|
|
} else {
|
|
pp -= 40; // 41 parameters
|
|
WalkingActor(pp[0], (SCNHANDLE *)&pp[1]);
|
|
return -41;
|
|
}
|
|
|
|
case WALKPOLY:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 1; // 2 parameters
|
|
WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
|
|
return -2;
|
|
} else {
|
|
pp -= 2; // 3 parameters
|
|
WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
|
|
return -3;
|
|
}
|
|
|
|
case WALKTAG:
|
|
// Common to both DW1 & DW2
|
|
if (TinselVersion >= 2) {
|
|
pp -= 1; // 2 parameters
|
|
WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
|
|
return -2;
|
|
} else {
|
|
pp -= 2; // 3 parameters
|
|
WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
|
|
return -3;
|
|
}
|
|
|
|
case WALKXPOS:
|
|
// DW2 only
|
|
pp[0] = WalkXPos();
|
|
return 0;
|
|
|
|
case WALKYPOS:
|
|
// DW2 only
|
|
pp[0] = WalkYPos();
|
|
return 0;
|
|
|
|
case WHICHCD:
|
|
// DW2 / Noir
|
|
pp[0] = WhichCd();
|
|
return 0;
|
|
|
|
case WHICHINVENTORY:
|
|
// Common to DW1 / DW2 / Noir
|
|
pp[0] = WhichInventory();
|
|
return 0;
|
|
|
|
case ZZZZZZ:
|
|
// DW2 only - dummy routine used during debugging
|
|
return -1;
|
|
|
|
default:
|
|
error("Unsupported library function");
|
|
}
|
|
|
|
//error("Can't possibly get here");
|
|
}
|
|
|
|
} // End of namespace Tinsel
|