mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-24 19:45:07 +00:00
1fffbe40ce
Previously, SetMidiVolume() would stop the currently playing track when the MIDI volume was set to 0. Now, the music track always plays, even when the volume is set to 0. This fixes bug #3541533 - "DW: Silencing music volume stops music" and resolves two FIXME comments
5662 lines
128 KiB
C++
5662 lines
128 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* 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/rince.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 "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 BG.CPP
|
|
extern void ChangePalette(SCNHANDLE hPal);
|
|
|
|
// 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 HANDLE.CPP
|
|
bool ValidHandle(SCNHANDLE offset);
|
|
|
|
// In SCENE.CPP
|
|
SCNHANDLE GetSceneHandle();
|
|
#endif
|
|
|
|
//----------------- GLOBAL GLOBAL DATA --------------------
|
|
|
|
// FIXME: Avoid non-const global vars
|
|
|
|
bool g_bEnableMenu;
|
|
|
|
static bool g_bInstantScroll = false;
|
|
static bool g_bEscapedCdPlay = false;
|
|
|
|
|
|
//----------------- 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,
|
|
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
|
|
};
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
// FIXME: Avoid non-const global vars
|
|
|
|
// 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 --------------------
|
|
|
|
/**
|
|
* For Scroll() and Offset(), work out top left for a
|
|
* given screen position.
|
|
*/
|
|
static void DecodeExtreme(EXTREME extreme, int *px, int *py) {
|
|
int Loffset, Toffset;
|
|
|
|
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
|
|
switch (extreme) {
|
|
case EX_BOTTOM:
|
|
*px = Loffset;
|
|
*py = BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_BOTTOMLEFT:
|
|
*px = 0;
|
|
*py = BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_BOTTOMRIGHT:
|
|
*px = BgWidth() - SCREEN_WIDTH;
|
|
*py = BgHeight() - SCREEN_HEIGHT;
|
|
break;
|
|
case EX_LEFT:
|
|
*px = 0;
|
|
*py = Toffset;
|
|
break;
|
|
case EX_RIGHT:
|
|
*px = 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 = 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;
|
|
};
|
|
typedef SCROLL_MONITOR *PSCROLL_MONITOR;
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
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 (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(POBJECT 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;
|
|
}
|
|
}
|
|
|
|
/********************************************************************\
|
|
|***** Library functions *****|
|
|
\********************************************************************/
|
|
|
|
/**
|
|
* Set actor's attributes.
|
|
* - currently only the text color.
|
|
*/
|
|
static void ActorAttr(int actor, int r1, int g1, int b1) {
|
|
storeActorAttr(actor, r1, g1, b1);
|
|
}
|
|
|
|
/**
|
|
* Behave as if actor has walked into a polygon with given brughtness.
|
|
*/
|
|
void ActorBrightness(int actor, int brightness) {
|
|
PMOVER 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) {
|
|
PMOVER 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) {
|
|
PMOVER pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
StoreMoverPalette(pMover, startColor, length);
|
|
}
|
|
|
|
/**
|
|
* Set actor's Z-factor.
|
|
*/
|
|
static void ActorPriority(int actor, int zFactor) {
|
|
SetActorZfactor(actor, zFactor);
|
|
}
|
|
|
|
/**
|
|
* Set actor's text color.
|
|
*/
|
|
static void ActorRGB(int actor, COLORREF color) {
|
|
SetActorRGB(actor, color);
|
|
}
|
|
|
|
/**
|
|
* Return the actor's scale.
|
|
*/
|
|
static int ActorScale(int actor) {
|
|
PMOVER 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;
|
|
|
|
GetActorPos(actor, &x, &y);
|
|
return (xory == ACTORXPOS) ? x : y;
|
|
}
|
|
|
|
/**
|
|
* Make all actors alive at the start of each scene.
|
|
*/
|
|
static void ActorsOn() {
|
|
setactorson();
|
|
}
|
|
|
|
/**
|
|
* Adds an icon to the conversation window.
|
|
*/
|
|
static void AddTopic(int icon) {
|
|
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_OPEN || invno == INV_DEFAULT);
|
|
|
|
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) {
|
|
PMOVER 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 Background(CORO_PARAM, SCNHANDLE bfilm) {
|
|
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) {
|
|
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) {
|
|
PMOVER pMover; // for if it's a moving actor
|
|
|
|
// Only do it if escaped!
|
|
if (myEscape && myEscape != GetEscEvents()) {
|
|
// End current graphic
|
|
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 (TinselV2) {
|
|
if (myEscape && myEscape != GetEscEvents()) {
|
|
g_bEscapedCdPlay = true;
|
|
return;
|
|
}
|
|
|
|
LoadExtraGraphData(start, next);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the hooked scene (if any)
|
|
*/
|
|
static void ClearHookScene() {
|
|
SetHookScene(0, 0, TRANS_DEF);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void CloseInventory() {
|
|
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 (TinselV2) {
|
|
if (param)
|
|
ControlOn();
|
|
else {
|
|
ControlOff();
|
|
|
|
switch (WhichInventoryOpen()) {
|
|
case INV_1:
|
|
case INV_2:
|
|
case INV_MENU:
|
|
KillInventory();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Tinsel 1 handling code
|
|
g_bEnableMenu = false;
|
|
|
|
switch (param) {
|
|
case CONTROL_STARTOFF:
|
|
GetControlToken(); // Take control
|
|
DisableTags(); // Switch off tags
|
|
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
|
|
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)
|
|
GetCursorXYNoWait(&g_controlX, &g_controlY, true); // Store cursor position
|
|
|
|
g_offtype = param;
|
|
|
|
if (param == CONTROL_OFF)
|
|
DwHideCursor(); // Blank out cursor
|
|
else if (param == CONTROL_OFFV) {
|
|
UnHideCursor();
|
|
FreezeCursor();
|
|
} else if (param == CONTROL_OFFV2) {
|
|
UnHideCursor();
|
|
}
|
|
break;
|
|
|
|
case CONTROL_ON:
|
|
if (g_offtype != CONTROL_OFFV2 && g_offtype != CONTROL_STARTOFF)
|
|
SetCursorXY(g_controlX, g_controlY);// ... where it was
|
|
|
|
FreeControlToken(); // Release control
|
|
|
|
if (!InventoryActive())
|
|
EnableTags(); // Tags back on
|
|
|
|
RestoreMainCursor(); // Re-instate cursor...
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
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 (TinselV2 && 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 (IsConvWindow())
|
|
return;
|
|
|
|
KillInventory();
|
|
|
|
if (TinselV2) {
|
|
// 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
|
|
SetConvDetails((CONV_PARAM)fn, hp, actor);
|
|
} else {
|
|
convPos(fn);
|
|
ConvPoly(hp);
|
|
}
|
|
|
|
PopUpInventory(INV_CONV); // Conversation window
|
|
ConvAction(INV_OPENICON); // CONVERSATION event
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Add icon to conversation window's permanent default list.
|
|
*/
|
|
static void ConvTopic(int icon) {
|
|
PermaConvIcon(icon);
|
|
}
|
|
|
|
/**
|
|
* Cursor(on/off)
|
|
*/
|
|
void Cursor(int onoff) {
|
|
if (onoff) {
|
|
// Re-instate cursor
|
|
UnHideCursor();
|
|
} else {
|
|
// Blank out cursor
|
|
DwHideCursor();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the x or y position of the cursor.
|
|
*/
|
|
static int CursorPos(int xory) {
|
|
int x, y;
|
|
|
|
GetCursorXY(&x, &y, true);
|
|
return (xory == CURSORXPOS) ? x : y;
|
|
}
|
|
|
|
/**
|
|
* Declare conversation window.
|
|
*/
|
|
static void DecConvW(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
|
|
idec_convw(text, MaxContents, MinWidth, MinHeight,
|
|
StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Declare config strings.
|
|
*/
|
|
static void DecCStrings(SCNHANDLE *tp) {
|
|
setConfigStrings(tp);
|
|
}
|
|
|
|
/**
|
|
* Declare cursor's reels.
|
|
*/
|
|
static void DecCursor(SCNHANDLE hFilm) {
|
|
DwInitCursor(hFilm);
|
|
}
|
|
|
|
/**
|
|
* Declare the language flags.
|
|
*/
|
|
static void DecFlags(SCNHANDLE hFilm) {
|
|
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) {
|
|
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) {
|
|
idec_inv2(text, MaxContents, MinWidth, MinHeight,
|
|
StartWidth, StartHeight, MaxWidth, MaxHeight);
|
|
}
|
|
|
|
/**
|
|
* Declare the bits that the inventory windows are constructed from.
|
|
*/
|
|
static void DecInvW(SCNHANDLE hf) {
|
|
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) {
|
|
PMOVER pMover; // Moving actor structure
|
|
|
|
if (TinselV2) {
|
|
// Tinsel 2 only specifies the lead actor Id
|
|
SetLeadId(id);
|
|
RegisterMover(id);
|
|
|
|
} else {
|
|
|
|
Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged
|
|
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) {
|
|
PMOVER 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);
|
|
}
|
|
|
|
/**
|
|
* Declare the text font.
|
|
*/
|
|
static void DecTagFont(SCNHANDLE hf) {
|
|
SetTagFontHandle(hf); // Store the font handle
|
|
if (TinselV0)
|
|
SetTalkFontHandle(hf); // Also re-use for talk text
|
|
}
|
|
|
|
/**
|
|
* Declare the text font.
|
|
*/
|
|
static void DecTalkFont(SCNHANDLE hf) {
|
|
SetTalkFontHandle(hf); // Store the font handle
|
|
}
|
|
|
|
/**
|
|
* Remove an icon from the conversation window.
|
|
*/
|
|
static void DelIcon(int icon) {
|
|
RemFromInventory(INV_CONV, icon);
|
|
}
|
|
|
|
/**
|
|
* Delete the object from inventory 1 or 2.
|
|
*/
|
|
static void DelInv(int object) {
|
|
if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
|
|
RemFromInventory(INV_2, object); // ...or 2 (whichever)
|
|
|
|
DropItem(object); // Stop holding it
|
|
}
|
|
|
|
/**
|
|
* DelTopic
|
|
*/
|
|
static void DelTopic(int icon) {
|
|
RemFromInventory(INV_CONV, icon);
|
|
}
|
|
|
|
/**
|
|
* DimMusic
|
|
*/
|
|
static void DimMusic() {
|
|
_vm->_pcmMusic->dim(true);
|
|
}
|
|
|
|
/**
|
|
* Delete the object from inventory 1 or 2.
|
|
*/
|
|
static void Drop(int object) {
|
|
if (object == -1)
|
|
object = HeldObject();
|
|
|
|
if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
|
|
RemFromInventory(INV_2, object); // ...or 2 (whichever)
|
|
|
|
DropItem(object); // Stop holding it
|
|
}
|
|
|
|
/**
|
|
* Delete all objects from inventory 1 and 2.
|
|
*/
|
|
static void DropEverything() {
|
|
HoldItem(NOOBJECT, false);
|
|
|
|
ClearInventory(INV_1);
|
|
ClearInventory(INV_2);
|
|
}
|
|
|
|
/**
|
|
* EnableMenu
|
|
*/
|
|
static void EnableMenu() {
|
|
g_bEnableMenu = true;
|
|
}
|
|
|
|
/**
|
|
* Kill an actor's current graphics.
|
|
*/
|
|
static void EndActor(int 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) {
|
|
PMOVER 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);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* FadeIn
|
|
*/
|
|
static void FadeIn() {
|
|
FadeInMedium(NULL);
|
|
}
|
|
|
|
/**
|
|
* FadeOut
|
|
*/
|
|
static void FadeOut() {
|
|
FadeOutMedium(NULL);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
DoFreezeCursor(bFreeze);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static int GetInvLimit(int invno) {
|
|
return 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);
|
|
CreateGhostPalette(BgPal());
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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 (InventoryPos(object) != NOOBJECT);
|
|
}
|
|
|
|
/**
|
|
* Returns which object is currently held.
|
|
*/
|
|
static int HeldObject() {
|
|
return 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) {
|
|
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) {
|
|
SetObjectFilm(object, hFilm);
|
|
}
|
|
|
|
/**
|
|
* See if an object is in the inventory.
|
|
*/
|
|
int InInventory(int object) {
|
|
return (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
|
|
|
|
PopUpInventory(invno);
|
|
}
|
|
|
|
/**
|
|
* Alter inventory object's icon.
|
|
*/
|
|
static void InvPlay(int object, SCNHANDLE hFilm) {
|
|
SetObjectFilm(object, hFilm);
|
|
}
|
|
|
|
/**
|
|
* See if an object is in the inventory.
|
|
*/
|
|
static int InWhichInv(int object) {
|
|
if (WhichItemHeld() == object)
|
|
return 0;
|
|
|
|
if (IsInInventory(object, INV_1))
|
|
return 1;
|
|
|
|
if (IsInInventory(object, INV_2))
|
|
return 2;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Kill an actor.
|
|
*/
|
|
static void KillActor(int 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;
|
|
|
|
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
return (lort == SCREENXPOS) ? Loffset : Toffset;
|
|
}
|
|
|
|
/**
|
|
* Set new cursor position.
|
|
*/
|
|
static void MoveCursor(int x, int y) {
|
|
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 (TinselV2) {
|
|
if (_vm->_bmv->MoviePlaying()) {
|
|
_vm->_bmv->AbortMovie();
|
|
CORO_SLEEP(2);
|
|
}
|
|
}
|
|
|
|
SetNewScene(scene, entrance, transition);
|
|
|
|
// Prevent tags and cursor re-appearing
|
|
if (TinselV2)
|
|
ControlStartOff();
|
|
else
|
|
GetControl(CONTROL_STARTOFF);
|
|
|
|
if (TinselV1)
|
|
++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) {
|
|
SetNoScroll(x1, y1, x2, y2);
|
|
}
|
|
|
|
/**
|
|
* Hold the specified object.
|
|
*/
|
|
static void ObjectHeld(int object) {
|
|
HoldItem(object);
|
|
}
|
|
|
|
/**
|
|
* Set the top left offset of the screen.
|
|
*/
|
|
void Offset(EXTREME extreme, int x, int y) {
|
|
KillScroll();
|
|
|
|
if (TinselV2)
|
|
DecodeExtreme(extreme, &x, &y);
|
|
|
|
PlayfieldSetPos(FIELD_WORLD, x, y);
|
|
}
|
|
|
|
/**
|
|
* OtherObject()
|
|
*/
|
|
int OtherObject(INV_OBJECT *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(GetIcon() == pinvo->id || WhichItemHeld() == pinvo->id);
|
|
|
|
if (GetIcon() == pinvo->id)
|
|
return WhichItemHeld();
|
|
else
|
|
return 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 && !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));
|
|
} 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));
|
|
}
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Play a film
|
|
*/
|
|
static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myEscape,
|
|
bool bTop, TINSEL_EVENT event, HPOLYGON hPoly, int taggedActor) {
|
|
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 (event == TALKING) {
|
|
int actor;
|
|
if (hPoly == NOPOLY) {
|
|
// Must be a tagged actor
|
|
|
|
assert(taggedActor && 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;
|
|
}
|
|
|
|
SetActorTalking(actor, true);
|
|
SetActorTalkFilm(actor, hFilm);
|
|
}
|
|
|
|
if (bComplete) {
|
|
// Play to completion before returning
|
|
CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, 0, false, false, myEscape != 0, myEscape, bTop));
|
|
} else {
|
|
// Kick off the play and return.
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, myEscape, bTop));
|
|
}
|
|
|
|
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);
|
|
|
|
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 (!MidiPlaying() && TinselV2)
|
|
CORO_SLEEP(1);
|
|
|
|
if (complete) {
|
|
while (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++) {
|
|
DwHideCursor();
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Play some music
|
|
*/
|
|
static void PlayMusic(int tune) {
|
|
_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(IsTaggedActor(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(IsTaggedActor(actor));
|
|
}
|
|
|
|
if (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 (!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 (TinselV2)
|
|
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 = NULL;
|
|
_ctx->bSample = false;
|
|
|
|
// Don't do it if it's not wanted
|
|
if (escOn && myEscape != GetEscEvents())
|
|
return;
|
|
|
|
if (!TinselV2) {
|
|
// 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, TextBufferAddr(), TBUFSZ);
|
|
|
|
// Calculate display time
|
|
bJapDoPrintText = false;
|
|
if (time == 0) {
|
|
// This is a 'talky' print
|
|
_ctx->time = TextTime(TextBufferAddr());
|
|
|
|
// Cut short-able if sustain was not set
|
|
_ctx->myleftEvent = bSustain ? 0 : GetLeftEvents();
|
|
} else {
|
|
_ctx->time = time * ONE_SECOND;
|
|
_ctx->myleftEvent = (TinselV2 && !bSustain) ? GetLeftEvents() : 0;
|
|
if (isJapanMode())
|
|
bJapDoPrintText = true;
|
|
}
|
|
|
|
// Print the text
|
|
if (TinselV2) {
|
|
int Loffset, Toffset;
|
|
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
|
|
TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(),
|
|
TXT_CENTER, 0);
|
|
assert(_ctx->pText);
|
|
|
|
// Adjust x, y, or z if necessary
|
|
KeepOnScreen(_ctx->pText, &x, &y);
|
|
if (IsTopWindow())
|
|
MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT);
|
|
|
|
} else if (bJapDoPrintText || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
|
|
int Loffset, Toffset; // Screen position
|
|
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
|
|
0, x - Loffset, y - Toffset,
|
|
TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTER);
|
|
assert(_ctx->pText); // string produced NULL text
|
|
if (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 >= BgWidth()) // Not off right
|
|
MultiMoveRelXY(_ctx->pText, 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 > BgHeight()) // Not off bottom
|
|
MultiMoveRelXY(_ctx->pText, 0, 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 (TinselV2) {
|
|
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
|
|
if (_ctx->pText != NULL)
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
|
|
_vm->_mixer->stopHandle(_ctx->handle);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *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 INV_OBJECT *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;
|
|
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 (TinselV2 && (myEscape) && (myEscape != GetEscEvents()))
|
|
return;
|
|
|
|
/*
|
|
* Find out which icon the cursor is over, and where to put the text.
|
|
*/
|
|
GetCursorXY(&_ctx->textx, &_ctx->texty, false); // Cursor position..
|
|
_ctx->item = 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 (!TinselV2)
|
|
g_bNotPointedRunning = false; // There may have been an OFF without an ON
|
|
|
|
// Make multi-ones escape
|
|
if (TinselV2 && (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 < (TinselV2 ? 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 || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
|
|
int xshift;
|
|
|
|
// Get the text string
|
|
if (TinselV2)
|
|
LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
|
|
else
|
|
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
|
|
|
|
_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
|
|
0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTER);
|
|
assert(_ctx->pText); // PrintObj() string produced NULL text
|
|
|
|
MultiSetZPosition(_ctx->pText, Z_INV_ITEXT);
|
|
|
|
if (TinselV2)
|
|
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 = NULL;
|
|
|
|
if (TinselV2) {
|
|
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 (WhichItemHeld() == pinvo->id)
|
|
break;
|
|
|
|
// Give way to non-POINTED-generated text
|
|
if (g_bNotPointedRunning) {
|
|
// Delete the text, and wait for the all-clear
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
|
|
_ctx->pText = NULL;
|
|
|
|
while (g_bNotPointedRunning)
|
|
CORO_SLEEP(1);
|
|
|
|
GetCursorXY(&x, &y, false);
|
|
if (InvItem(&x, &y, false) != _ctx->item)
|
|
break;
|
|
|
|
// Re-display in the same place
|
|
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
|
|
_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
|
|
TextBufferAddr(), 0, _ctx->textx, _ctx->texty, 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
|
|
GetCursorXY(&x, &y, false);
|
|
|
|
} while (InvItemId(x, y) == pinvo->id);
|
|
} else {
|
|
/*
|
|
* PrintObj() called from other event
|
|
*/
|
|
_ctx->myEscape = GetLeftEvents();
|
|
_ctx->bTookControl = GetControl();
|
|
|
|
// Display for a time, but abort if conversation gets hidden
|
|
if (_ctx->pText)
|
|
_ctx->ticks = TextTime(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->myEscape)
|
|
|| --_ctx->timeout <= 0
|
|
|| 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
|
|
if (_ctx->pText)
|
|
MultiDeleteObject(GetPlayfieldList(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 INV_OBJECT *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 (WhichItemHeld() == pinvo->id)
|
|
break;
|
|
|
|
// Give way to non-POINTED-generated text
|
|
if (g_bNotPointedRunning) {
|
|
// Delete the text, and wait for the all-clear
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), pText);
|
|
pText = NULL;
|
|
while (g_bNotPointedRunning)
|
|
CORO_SLEEP(1);
|
|
|
|
GetCursorXY(&x, &y, false);
|
|
if (InvItem(&x, &y, false) != item)
|
|
break;
|
|
|
|
// Re-display in the same place
|
|
LoadStringRes(text, TextBufferAddr(), TBUFSZ);
|
|
pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
|
|
0, textx, texty, 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
|
|
GetCursorXY(&x, &y, false);
|
|
} while (InvItemId(x, y) == pinvo->id);
|
|
|
|
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 (isJapanMode())
|
|
_ctx->ticks = JAP_TEXT_TIME;
|
|
else if (pText)
|
|
_ctx->ticks = TextTime(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 || 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) || (TinselV2 && (actor != 0)));
|
|
|
|
if (hp != NOPOLY) {
|
|
// Poly handling
|
|
if (TinselV2)
|
|
SetPolyTagWanted(hp, true, bCursor, text);
|
|
else if (PolyTagState(hp) == TAG_OFF) {
|
|
SetPolyTagState(hp, TAG_ON);
|
|
SetPolyTagHandle(hp, text);
|
|
}
|
|
} else {
|
|
// Moving actor handling
|
|
SetActorTagWanted(actor, true, bCursor, text);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Quits the game
|
|
*/
|
|
static void QuitGame() {
|
|
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
|
|
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 (TinselV2) {
|
|
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 (TinselV2) {
|
|
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 GetIcon();
|
|
}
|
|
|
|
/**
|
|
* Scroll the screen to target co-ordinates.
|
|
*/
|
|
static void Scroll(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 ((TinselV2 && g_bInstantScroll) || (escOn && myEscape != GetEscEvents())) {
|
|
// Instant completion!
|
|
Offset(extreme, _ctx->x, _ctx->y);
|
|
} else {
|
|
_ctx->thisScroll = g_scrollNumber;
|
|
if (TinselV2)
|
|
DecodeExtreme(extreme, &_ctx->x, &_ctx->y);
|
|
|
|
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();
|
|
|
|
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
|
|
} while (Loffset != _ctx->x || Toffset != _ctx->y);
|
|
} else if (TinselV2 && 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) {
|
|
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 (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) {
|
|
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) {
|
|
InvSetLimit(invno, n);
|
|
}
|
|
|
|
/**
|
|
* Guess what.
|
|
*/
|
|
static void SetInvSize(int invno, int MinWidth, int MinHeight,
|
|
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
|
|
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;
|
|
|
|
ChangePalette(hPal);
|
|
}
|
|
|
|
/**
|
|
* 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, TextBufferAddr(), TBUFSZ);
|
|
error("Tried to execute shell command \"%s\"", 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() {
|
|
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;
|
|
PMOVER pMover; // Moving actor structure
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->pMover = GetMover(actor);
|
|
assert(!TinselV2 || (_ctx->pMover != NULL));
|
|
|
|
if (_ctx->pMover) {
|
|
if (TinselV2) {
|
|
// New special. If no paths, just ignore this
|
|
if (PathCount() == 0)
|
|
return;
|
|
|
|
// Another new special.
|
|
// If lead actor, and TalkVia, ignore
|
|
if ((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) ? 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 (TinselV2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, AWAY);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_DOWN) {
|
|
if (TinselV2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, FORWARD);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_LEFT) {
|
|
if (TinselV2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, LEFTREEL);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm == TF_RIGHT) {
|
|
if (TinselV2) CORO_GIVE_WAY;
|
|
SetMoverDirection(_ctx->pMover, RIGHTREEL);
|
|
SetMoverStanding(_ctx->pMover);
|
|
} else if (hFilm != TF_NONE) {
|
|
if (TinselV2) 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));
|
|
}
|
|
|
|
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 (TinselV2) {
|
|
if (actor != LEAD_ACTOR && 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 == 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() {
|
|
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) {
|
|
PMOVER pMover;
|
|
|
|
pMover = GetMover(actor);
|
|
assert(pMover);
|
|
|
|
if (TinselV2) {
|
|
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 (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 (TinselV2) {
|
|
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 == GetLeadId() || actor == LEAD_ACTOR) {
|
|
_ctx->bTookControl = GetControl(CONTROL_OFFV2);
|
|
if (TinselV2 && _ctx->bTookControl)
|
|
RestoreMainCursor();
|
|
} else {
|
|
_ctx->bTookControl = false;
|
|
}
|
|
|
|
if (TinselV2 && (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 (TinselV2 && (zOverride != -1)) {
|
|
PMOVER 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) {
|
|
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, PMOVER pMover, int actor) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (pMover) {
|
|
SetMoverStanding(pMover);
|
|
AlterMover(pMover, 0, AR_POPREEL);
|
|
} else {
|
|
SetActorTalking(actor, false);
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false));
|
|
}
|
|
|
|
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
|
|
PMOVER 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 = NULL;
|
|
|
|
// If waiting is enabled, wait for ongoing scroll
|
|
if (TinselV2 && 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 (!TinselV2 && (actorId && !actorAlive(actorId)))
|
|
CORO_KILL_SELF();
|
|
|
|
if (!TinselV2 || (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 = 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 (TinselV2)
|
|
_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 == GetLeadId() || (TinselV2 && (_ctx->actor == LEAD_ACTOR)))
|
|
_ctx->bTookControl = GetControl(CONTROL_OFF);
|
|
else
|
|
_ctx->bTookControl = false;
|
|
_ctx->bTookTags = DisableTagsIfEnabled();
|
|
|
|
if (TinselV2) {
|
|
/*
|
|
* Divert stuff
|
|
*/
|
|
if (SysVar(ISV_DIVERT_ACTOR) && (_ctx->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 (!TinselV2) {
|
|
_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 {
|
|
SetActorTalking(_ctx->actor, true);
|
|
SetActorTalkFilm(_ctx->actor, hFilm);
|
|
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, -1, -1, 0, false, 0, escOn, myEscape, false));
|
|
}
|
|
_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 (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 (TinselV2)
|
|
// 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 (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->escEvents)
|
|
_ctx->escEvents = GetEscEvents();
|
|
|
|
for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
|
|
if (TinselV2 && _ctx->escEvents && _ctx->escEvents != GetEscEvents())
|
|
break;
|
|
|
|
/*
|
|
* Display the text.
|
|
*/
|
|
_ctx->bSample = _ctx->bSamples;
|
|
_ctx->pText = NULL;
|
|
|
|
if (isJapanMode()) {
|
|
_ctx->ticks = JAP_TEXT_TIME;
|
|
} else if (_vm->_config->_useSubtitles || !_ctx->bSample) {
|
|
/*
|
|
* Work out where to display the text
|
|
*/
|
|
int xshift, yshift;
|
|
|
|
PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
|
|
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK))
|
|
GetActorMidTop(_ctx->actor, &_ctx->x, &_ctx->y);
|
|
|
|
if (!TinselV0)
|
|
SetTextPal(GetActorRGB(_ctx->actor));
|
|
if (TinselV2)
|
|
LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
|
|
else {
|
|
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
|
|
|
|
_ctx->y -= _ctx->Toffset;
|
|
}
|
|
|
|
_ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS),
|
|
TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset,
|
|
GetTalkFontHandle(), TXT_CENTER);
|
|
assert(_ctx->pText); // talk() string produced NULL text;
|
|
|
|
if (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 (TinselV2)
|
|
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 (TinselV2)
|
|
// 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(TextBufferAddr());
|
|
}
|
|
|
|
if (TinselV2 && _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;
|
|
|
|
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 (TinselV2) {
|
|
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 (!TinselV2 || (_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);
|
|
|
|
if (_ctx->pText != NULL) {
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
|
|
_ctx->pText = NULL;
|
|
}
|
|
if (TinselV2 && _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);
|
|
if (_ctx->pText != NULL)
|
|
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
|
|
|
|
if (TinselV2) {
|
|
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
|
|
SetActorTalking(_ctx->actor, false);
|
|
if (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 (TinselV2) 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 (!isJapanMode() && (_vm->_config->_useSubtitles || !_vm->_sound->sampleExists(text)))
|
|
SetTextPal(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 (!isJapanMode())
|
|
SetTextPal(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 (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);
|
|
}
|
|
|
|
/**
|
|
* Declare a temporary text font.
|
|
*/
|
|
static void TempTagFont(SCNHANDLE hFilm) {
|
|
SetTempTagFontHandle(hFilm); // Store the font handle
|
|
}
|
|
|
|
/**
|
|
* Declare a temporary text font.
|
|
*/
|
|
static void TempTalkFont(SCNHANDLE hFilm) {
|
|
SetTempTalkFontHandle(hFilm); // Store the font handle
|
|
}
|
|
|
|
/**
|
|
* ThisObject
|
|
*/
|
|
static int ThisObject(INV_OBJECT *pinvo) {
|
|
assert(pinvo != NULL);
|
|
|
|
return pinvo->id;
|
|
}
|
|
|
|
/**
|
|
* 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 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 = (TinselV2 && (bpos != 0)) || (!TinselV2 && (bpos == TW_START));
|
|
|
|
KillInventory();
|
|
|
|
if (isStart)
|
|
OpenMenu(TOP_WINDOW);
|
|
}
|
|
|
|
/**
|
|
* TranslucentIndex
|
|
*/
|
|
static void TranslucentIndex(unsigned index) {
|
|
assert(index <= 255);
|
|
|
|
SetTranslucencyOffset(index);
|
|
}
|
|
|
|
/**
|
|
* Play a sample.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* UnDimMusic
|
|
*/
|
|
static void UnDimMusic() {
|
|
_vm->_pcmMusic->unDim(true);
|
|
}
|
|
|
|
/**
|
|
* unhookscene
|
|
*/
|
|
static void UnHookSceneFn() {
|
|
UnHookScene();
|
|
}
|
|
|
|
/**
|
|
* Un-define an actor as tagged.
|
|
*/
|
|
static void UnTagActorFn(int 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 (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 (TinselV1) {
|
|
// Store cursor position
|
|
while (!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 (TinselV1) {
|
|
int curX, curY;
|
|
GetCursorXY(&curX, &curY, false); // Store cursor position
|
|
if (curX != _ctx->startX || curY != _ctx->startY)
|
|
break;
|
|
}
|
|
|
|
if (MenuActive())
|
|
break;
|
|
}
|
|
|
|
if (!MenuActive())
|
|
return;
|
|
|
|
do {
|
|
CORO_SLEEP(1);
|
|
} while (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 (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;
|
|
PMOVER 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 (TinselV2)
|
|
StopMover(pMover);
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
|
|
return;
|
|
}
|
|
|
|
if (TinselV2) {
|
|
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 (TinselV2) {
|
|
// Croak if he is doing an SWalk()
|
|
if (MoverIsSWalking(pMover))
|
|
CORO_KILL_SELF();
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, x, y, igPath, hFilm);
|
|
SetMoverZoverride(pMover, zOverride);
|
|
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);
|
|
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);
|
|
|
|
PMOVER 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 (TinselV2) {
|
|
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 aquire token to kill off any other normal walk
|
|
GetToken(pMover->actorToken);
|
|
FreeToken(pMover->actorToken);
|
|
}
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, x, y, false, film);
|
|
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) {
|
|
PMOVER pActor; // Moving actor structure
|
|
|
|
if (TinselVersion == TINSEL_V2) {
|
|
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
|
|
PMOVER 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 (TinselV2) {
|
|
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);
|
|
DoScrollCursor();
|
|
|
|
while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) {
|
|
CORO_SLEEP(1);
|
|
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
// Straight there if escaped
|
|
StandTag(actor, hp);
|
|
if (!TinselV2)
|
|
FreeToken(pMover->actorToken);
|
|
return;
|
|
}
|
|
|
|
// Die if superceded
|
|
if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
|
|
CORO_KILL_SELF();
|
|
}
|
|
|
|
if (!TinselV2)
|
|
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);
|
|
|
|
PMOVER 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 (!TinselV2)
|
|
GetToken(pMover->actorToken);
|
|
else {
|
|
if (MoverHidden(pMover))
|
|
return;
|
|
}
|
|
|
|
GetPolyNode(hp, &pnodex, &pnodey);
|
|
|
|
_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
|
|
DoScrollCursor();
|
|
|
|
while (MoverMoving(pMover)) {
|
|
if (escOn && myEscape != GetEscEvents()) {
|
|
// Straight there if escaped
|
|
StandTag(actor, hp);
|
|
if (!TinselV2)
|
|
FreeToken(pMover->actorToken);
|
|
return;
|
|
}
|
|
|
|
CORO_SLEEP(1);
|
|
|
|
// Die if superceded
|
|
if (TinselV2 && (_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 == GetLeadId())
|
|
AlterMover(pMover, pFilm, AR_NORMAL);
|
|
else
|
|
SetMoverStanding(pMover);
|
|
break;
|
|
}
|
|
|
|
if (!TinselV2)
|
|
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 WhichInventoryOpen();
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 (TinselV0) libCode = DW1DEMO_CODES[operand];
|
|
else if (!TinselV2) libCode = DW1_CODES[operand];
|
|
else if (TinselV2Demo) libCode = DW2DEMO_CODES[operand];
|
|
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 only
|
|
pp -= 1; // 2 parameters
|
|
ActorPriority(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case ACTORREF:
|
|
// Common to both DW1 & DW2
|
|
if (!TinselV0)
|
|
error("actorref isn't a real function");
|
|
return 0;
|
|
|
|
case ACTORRGB:
|
|
// DW2 only
|
|
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 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 both DW1 & DW2
|
|
AddInv(INV_1, pp[0]);
|
|
return -1;
|
|
|
|
case ADDINV2:
|
|
// Common to both DW1 & DW2
|
|
AddInv(INV_2, pp[0]);
|
|
return -1;
|
|
|
|
case ADDOPENINV:
|
|
// Common to both DW1 & DW2
|
|
AddInv(TinselV2 ? 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 both DW1 & DW2
|
|
Background(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
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 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 ? 1 : 0;
|
|
}
|
|
|
|
if (!pp[0])
|
|
KillSelf(coroParam);
|
|
return -2;
|
|
|
|
case CAMERA:
|
|
// Common to both DW1 & DW2
|
|
Camera(pp[0]);
|
|
return -1;
|
|
|
|
case CDCHANGESCENE:
|
|
// DW2 only
|
|
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 both DW1 & DW2
|
|
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 CURSOR:
|
|
// DW2 only
|
|
Cursor(pp[0]);
|
|
return -1;
|
|
|
|
case CURSORXPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = CursorPos(CURSORXPOS);
|
|
return 0;
|
|
|
|
case CURSORYPOS:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = CursorPos(CURSORYPOS);
|
|
return 0;
|
|
|
|
case CUTSCENE:
|
|
// DW1 only
|
|
error("cutscene isn't a real function");
|
|
|
|
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 (TinselV2)
|
|
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 both DW1 & DW2
|
|
pp -= 7; // 8 parameters
|
|
DecInv2(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:
|
|
// DW2 only
|
|
pp -= 2; // 3 parameters
|
|
DeclareLanguage(pp[0], pp[1], pp[2]);
|
|
return -3;
|
|
|
|
case DECLEAD:
|
|
// Common to both DW1 & DW2
|
|
if (TinselV2) {
|
|
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 both DW1 & DW2
|
|
DecTagFont(pp[0]);
|
|
return -1;
|
|
|
|
case DECTALKFONT:
|
|
// Common to both DW1 & DW2
|
|
DecTalkFont(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
|
|
DimMusic();
|
|
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 only
|
|
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 both DW1 & DW2
|
|
if (TinselVersion == TINSEL_V2)
|
|
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
|
|
FadeIn();
|
|
return 0;
|
|
|
|
case FADEMIDI:
|
|
// DW1 only
|
|
FadeMidi(coroParam, pp[0]);
|
|
return -1;
|
|
|
|
case FADEOUT:
|
|
// DW1 only
|
|
FadeOut();
|
|
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 only
|
|
pp[0] = Have(pp[0]);
|
|
return 0; // using return value
|
|
|
|
case HELDOBJECT:
|
|
// Common to both DW1 & DW2
|
|
pp[0] = HeldObject();
|
|
return 0;
|
|
|
|
case HIDEACTOR:
|
|
// Common to both DW1 & DW2
|
|
if (!TinselV2)
|
|
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 only
|
|
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 (TinselV2)
|
|
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 only
|
|
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 both DW1 & DW2
|
|
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 both DW1 & DW2
|
|
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 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 (TinselV2) {
|
|
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 PAUSE:
|
|
// DW2 only
|
|
WaitTime(coroParam, 1, true, pic->escOn, pic->myEscape);
|
|
return 0;
|
|
|
|
case PLAY:
|
|
// Common to both DW1 & DW2
|
|
if (TinselV2) {
|
|
pp -= 3; // 4 parameters
|
|
if (*pResumeState == RES_1 && 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:
|
|
// DW2 only
|
|
PlayMovie(coroParam, pp[0], pic->myEscape);
|
|
return -1;
|
|
|
|
case PLAYMUSIC:
|
|
// DW2 only
|
|
PlayMusic(pp[0]);
|
|
return -1;
|
|
|
|
case PLAYRTF:
|
|
// Common to both DW1 & DW2
|
|
error("playrtf only applies to cdi");
|
|
|
|
case PLAYSAMPLE:
|
|
// Common to both DW1 & DW2
|
|
if (TinselV2) {
|
|
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 only
|
|
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 (TinselV2) {
|
|
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 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 both DW1 & DW2
|
|
PrintTag(pic->hPoly, pp[0], TinselV2 ? pic->idActor : 0, false);
|
|
return -1;
|
|
|
|
case QUITGAME:
|
|
// Common to both DW1 & DW2
|
|
QuitGame();
|
|
return 0;
|
|
|
|
case RANDOM:
|
|
// Common to both DW1 & DW2
|
|
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 (TinselV2) {
|
|
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 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 (TinselV2) {
|
|
pp -= 5; // 6 parameters
|
|
Scroll(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
|
|
Scroll(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 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 (TinselV2) {
|
|
// 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 SETSYSTEMSTRING:
|
|
// DW2 only
|
|
pp -= 1; // 2 parameters
|
|
SetSystemString(pp[0], pp[1]);
|
|
return -2;
|
|
|
|
case SETSYSTEMVAR:
|
|
// DW1 only
|
|
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 SHELL:
|
|
// DW2 only
|
|
Shell(pp[0]);
|
|
return 0;
|
|
|
|
case SHOWACTOR:
|
|
// DW2 only
|
|
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 only
|
|
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 only
|
|
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 both DW1 & DW2
|
|
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 only
|
|
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 (TinselV2) {
|
|
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 only
|
|
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 (TinselV2)
|
|
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 (TinselV2) {
|
|
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 only
|
|
TalkVia(pp[0]);
|
|
return -1;
|
|
|
|
case TEMPTAGFONT:
|
|
// DW2 only
|
|
TempTagFont(pp[0]);
|
|
return -1;
|
|
|
|
case TEMPTALKFONT:
|
|
// DW2 only
|
|
TempTalkFont(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 (TinselV2) {
|
|
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
|
|
UnDimMusic();
|
|
return 0;
|
|
|
|
case UNHOOKSCENE:
|
|
// Common to both DW1 & DW2
|
|
UnHookSceneFn();
|
|
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 both DW1 & DW2
|
|
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 (TinselV2) {
|
|
// 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 (TinselV2) {
|
|
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 (TinselV2) {
|
|
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 only
|
|
pp[0] = WhichCd();
|
|
return 0;
|
|
|
|
case WHICHINVENTORY:
|
|
// Common to both DW1 & DW2
|
|
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
|