diff options
Diffstat (limited to 'engines/tinsel/tinlib.cpp')
-rw-r--r-- | engines/tinsel/tinlib.cpp | 4535 |
1 files changed, 3578 insertions, 957 deletions
diff --git a/engines/tinsel/tinlib.cpp b/engines/tinsel/tinlib.cpp index 07c1b22b2a..2313f343e8 100644 --- a/engines/tinsel/tinlib.cpp +++ b/engines/tinsel/tinlib.cpp @@ -36,12 +36,16 @@ #include "tinsel/config.h" #include "tinsel/coroutine.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/inventory.h" +#include "tinsel/dialogs.h" +#include "tinsel/mareels.h" #include "tinsel/move.h" #include "tinsel/multiobj.h" #include "tinsel/music.h" @@ -49,6 +53,7 @@ #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" @@ -57,6 +62,7 @@ #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" @@ -72,49 +78,59 @@ namespace Tinsel { extern bool bRestart; // restart flag - set to restart the game extern bool bHasRestarted; // Set after a restart +// In PCODE.CPP +extern bool bNoPause; + // In DOS_MAIN.C // TODO/FIXME: From dos_main.c: "Only used on PSX so far" int clRunMode = 0; //----------------- EXTERNAL FUNCTIONS --------------------- -// in BG.C -extern void startupBackground(SCNHANDLE bfilm); +// in BG.CPP extern void ChangePalette(SCNHANDLE hPal); -extern int BackgroundWidth(void); -extern int BackgroundHeight(void); +extern int BgWidth(void); +extern int BgHeight(void); -// in DOS_DW.C -extern void SetHookScene(SCNHANDLE scene, int entrance, int transition); -extern void SetNewScene(SCNHANDLE scene, int entrance, int transition); -extern void UnHookScene(void); -extern void SuspendHook(void); -extern void UnSuspendHook(void); +// in BMV.CPP +void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape); +bool MoviePlaying(void); +void AbortMovie(void); -// in PDISPLAY.C +// in PDISPLAY.CPP extern void EnableTags(void); extern void DisableTags(void); bool DisableTagsIfEnabled(void); extern void setshowstring(void); -// in PLAY.C -extern void playFilm(SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn, int myescEvent, bool bTop); -extern void playFilmc(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, int sfact, bool escOn, int myescEvent, bool bTop); +// in SAVELOAD.CPP +extern int NewestSavedGame(void); -// in SCENE.C +// in SCENE.CPP extern void setshowpos(void); +// 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(void); +extern void SuspendHook(void); +extern void UnSuspendHook(void); + #ifdef BODGE -// In DOS_HAND.C +// In HANDLE.CPP bool ValidHandle(SCNHANDLE offset); -// In SCENE.C +// In SCENE.CPP SCNHANDLE GetSceneHandle(void); #endif //----------------- GLOBAL GLOBAL DATA -------------------- -bool bEnableF1; +bool bEnableMenu; + +static bool bInstantScroll = false; +static bool bEscapedCdPlay = false; //----------------- LOCAL DEFINES -------------------- @@ -125,70 +141,257 @@ bool bEnableF1; |* Library Procedure and Function codes *| \*----------------------------------------------------------------------*/ -enum LIB_CODE { - ACTORATTR = 0, ACTORDIRECTION, ACTORREF, ACTORSCALE, ACTORXPOS = 4, - ACTORYPOS, ADDICON, ADDINV1, ADDINV2, ADDOPENINV, AUXSCALE = 10, - BACKGROUND, CAMERA, CLOSEINVENTORY, CONTROL, CONVERSATION = 15, - CONVICON, CURSORXPOS, CURSORYPOS, DEC_CONVW, DEC_CURSOR = 20, - DEC_INV1, DEC_INV2, DEC_INVW, DEC_LEAD, DEC_TAGFONT = 25, - DEC_TALKFONT, DELICON, DELINV, EFFECTACTOR, ESCAPE, EVENT = 31, - GETINVLIMIT, HELDOBJECT, HIDE, ININVENTORY, INVDEPICT = 36, - INVENTORY, KILLACTOR, KILLBLOCK, KILLEXIT, KILLTAG, LEFTOFFSET = 42, - MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, PAUSE = 48, - PLAY, PLAYMIDI, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ = 54, - PRINTTAG, RANDOM, RESTORE_SCENE, SAVE_SCENE, SCALINGREELS = 59, - SCANICON, SCROLL, SETACTOR, SETBLOCK, SETEXIT, SETINVLIMIT = 65, - SETPALETTE, SETTAG, SETTIMER, SHOWPOS, SHOWSTRING, SPLAY = 71, - STAND, STANDTAG, STOP, SWALK, TAGACTOR, TALK, TALKATTR, TIMER = 79, - TOPOFFSET, TOPPLAY, TOPWINDOW, UNTAGACTOR, VIBRATE, WAITKEY = 85, - WAITTIME, WALK, WALKED, WALKINGACTOR, WALKPOLY, WALKTAG = 91, - WHICHINVENTORY = 92, - ACTORSON, CUTSCENE, HOOKSCENE, IDLETIME, RESETIDLETIME = 97, - TALKAT, UNHOOKSCENE, WAITFRAME, DEC_CSTRINGS, STOPMIDI, STOPSAMPLE = 103, - TALKATS = 104, - DEC_FLAGS, FADEMIDI, CLEARHOOKSCENE, SETINVSIZE, INWHICHINV = 109, - NOBLOCKING, SAMPLEPLAYING, TRYPLAYSAMPLE, ENABLEF1 = 113, - RESTARTGAME, QUITGAME, FRAMEGRAB, PLAYRTF, CDPLAY, CDLOAD = 119, - HASRESTARTED, RESTORE_CUT, RUNMODE, SUBTITLES, SETLANGUAGE = 124 +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 }; +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 +}; +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 -------------------- // 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 +// They are global so that MoveCursor(..) has a net effect if it // precedes control(on). static int controlX = 0, controlY = 0; -static int offtype = 0; // used by control() -static uint32 lastValue = 0; // used by dw_random() -static int scrollCount = 0; // used by scroll() +static int offtype = 0; // used by Control() +static uint32 lastValue = 0; // used by RandomFn() +static int scrollNumber = 0; // used by scroll() -static bool NotPointedRunning = false; // Used in printobj and printobjPointed +static bool bNotPointedRunning = false; // Used in Printobj and PrintObjPointed static COLORREF s_talkfontColor = 0; //----------------- FORWARD REFERENCES -------------------- -void resetidletime(void); -void stopmidi(void); -void stopsample(void); -void walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath, bool escOn, int myescTime); +static int HeldObject(void); +void Offset(EXTREME extreme, int x, int y); +static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape); +void ResetIdleTime(void); +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); +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 != 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 colours into the DAC queue. + * Poke supplied colour into the DAC queue. */ -static void setTextPal(COLORREF col) { +void SetTextPal(COLORREF col) { s_talkfontColor = col; - UpdateDACqueue(TALKFONT_COL, 1, &s_talkfontColor); + SetTalkColourRef(col); + UpdateDACqueue(TalkColour(), 1, &s_talkfontColor); } - +/** + * Work out a time depending on length of string and + * subtitle speed modification. + */ static int TextTime(char *pTstring) { if (isJapanMode()) return JAP_TEXT_TIME; @@ -198,45 +401,148 @@ static int TextTime(char *pTstring) { return strlen(pTstring) + ONE_SECOND + (speedText * 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 colour. */ -void actorattr(int actor, int r1, int g1, int b1) { +static void ActorAttr(int actor, int r1, int g1, int b1) { storeActorAttr(actor, r1, g1, b1); } /** - * Return the actor's direction. + * Behave as if actor has walked into a polygon with given brughtness. */ -int actordirection(int actor) { - PMACTOR pActor; +void ActorBrightness(int actor, int brightness) { + PMOVER pMover = GetMover(actor); - pActor = GetMover(actor); - assert(pActor != NULL); // not a moving actor + assert(pMover != NULL); + assert(brightness >= 0 && brightness <= 10); - return (int)GetMActorDirection(pActor); + MoverBrightness(pMover, brightness); } /** - * Return the actor's scale. + * 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 startColour, int length) { + PMOVER pMover = GetMover(actor); + assert(pMover); + + StoreMoverPalette(pMover, startColour, length); +} + +/** + * Set actor's Z-factor. */ -int actorscale(int actor) { - PMACTOR pActor; +static void ActorPriority(int actor, int zFactor) { + SetActorZfactor(actor, zFactor); +} - pActor = GetMover(actor); - assert(pActor != NULL); // not a moving actor +/** + * Set actor's text colour. + */ +static void ActorRGB(int actor, COLORREF colour) { + SetActorRGB(actor, colour); +} - return (int)GetMActorScale(pActor); +/** + * 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. */ -int actorpos(int xory, int actor) { +static int ActorPos(int xory, int actor) { int x, y; GetActorPos(actor, &x, &y); @@ -246,22 +552,23 @@ int actorpos(int xory, int actor) { /** * Make all actors alive at the start of each scene. */ -void actorson(void) { +static void ActorsOn(void) { setactorson(); } /** * Adds an icon to the conversation window. */ -void addicon(int icon) { +static void AddTopic(int icon) { AddToInventory(INV_CONV, icon, false); } /** * Place the object in inventory 1 or 2. */ -void addinv(int invno, int object) { - assert(invno == INV_1 || invno == INV_2 || invno == INV_OPEN); // illegal inventory number +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); } @@ -269,58 +576,104 @@ void addinv(int invno, int object) { /** * Define an actor's walk and stand reels for an auxilliary scale. */ -void auxscale(int actor, int scale, SCNHANDLE *rp) { - PMACTOR pActor; - - pActor = GetMover(actor); - assert(pActor); // Can't set aux scale for a non-moving actor +static void AuxScale(int actor, int scale, SCNHANDLE *rp) { + PMOVER pMover = GetMover(actor); + assert(pMover); int j; for (j = 0; j < 4; ++j) - pActor->WalkReels[scale-1][j] = *rp++; + pMover->walkReels[scale-1][j] = *rp++; for (j = 0; j < 4; ++j) - pActor->StandReels[scale-1][j] = *rp++; + pMover->standReels[scale-1][j] = *rp++; for (j = 0; j < 4; ++j) - pActor->TalkReels[scale-1][j] = *rp++; + pMover->talkReels[scale-1][j] = *rp++; } /** * Defines the background image for a scene. */ -void background(SCNHANDLE bfilm) { - startupBackground(bfilm); +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. */ -void camera(int actor) { +static void Camera(int actor) { ScrollFocus(actor); } /** + * Sets the CD Change Scene + */ + +static void CdChangeScene(SCNHANDLE hScene) { + SetCdChangeScene(hScene); +} + +/** + * CdDoChange + */ +void CdDoChange(CORO_PARAM) { + if (!GotoCD()) + return; + + CdCD(coroParam); + CdHasChanged(); +} + +/** + * 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. */ -void cdload(SCNHANDLE start, SCNHANDLE next) { +static void CDload(SCNHANDLE start, SCNHANDLE next, int myEscape) { assert(start && next && start != next); // cdload() fault -// TODO/FIXME -// LoadExtraGraphData(start, next); + if (TinselV2) { + if (myEscape && myEscape != GetEscEvents()) { + bEscapedCdPlay = true; + return; + } + + LoadExtraGraphData(start, next); + } } /** * Clear the hooked scene (if any) */ - -void clearhookscene() { - SetHookScene(0, 0, 0); +static void ClearHookScene() { + SetHookScene(0, 0, TRANS_DEF); } /** * Guess what. */ - -void closeinventory(void) { +static void CloseInventory(void) { KillInventory(); } @@ -328,9 +681,29 @@ void closeinventory(void) { * 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; + } + } -void control(int param) { - bEnableF1 = false; + return; + } + + // Tinsel 1 handling code + bEnableMenu = false; switch (param) { case CONTROL_STARTOFF: @@ -385,46 +758,82 @@ void control(int param) { /** * Open or close the conversation window. */ - -void conversation(int fn, HPOLYGON hp, bool escOn, int myescEvent) { +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); - switch (fn) { - case CONV_END: // Close down conversation + CORO_BEGIN_CODE(_ctx); + + if (fn == CONV_END) { + // Close down conversation CloseDownConv(); - break; + } 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); - case CONV_DEF: // Default (i.e. TOP of screen) - case CONV_BOTTOM: // BOTTOM of screen // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) - break; + if (escOn && myEscape != GetEscEvents()) + return; + // Don't do it if already in a conversation if (IsConvWindow()) - break; + return; KillInventory(); - convPos(fn); - ConvPoly(hp); + + 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 - break; } + + CORO_END_CODE; } /** * Add icon to conversation window's permanent default list. */ +static void ConvTopic(int icon) { + PermaConvIcon(icon); +} -void convicon(int icon) { - AddIconToPermanentDefaultList(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. */ - -int cursorpos(int xory) { +static int CursorPos(int xory) { int x, y; GetCursorXY(&x, &y, true); @@ -434,8 +843,7 @@ int cursorpos(int xory) { /** * Declare conversation window. */ - -void dec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight, +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); @@ -444,32 +852,28 @@ void dec_convw(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight, /** * Declare config strings. */ - -void dec_cstrings(SCNHANDLE *tp) { +static void DecCStrings(SCNHANDLE *tp) { setConfigStrings(tp); } /** * Declare cursor's reels. */ - -void dec_cursor(SCNHANDLE bfilm) { - DwInitCursor(bfilm); +static void DecCursor(SCNHANDLE hFilm) { + DwInitCursor(hFilm); } /** * Declare the language flags. */ - -void dec_flags(SCNHANDLE hf) { - setFlagFilms(hf); +static void DecFlags(SCNHANDLE hFilm) { + setFlagFilms(hFilm); } /** * Declare inventory 1's parameters. */ - -void dec_inv1(SCNHANDLE text, int MaxContents, +static void DecInv1(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight, int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) { @@ -480,8 +884,7 @@ void dec_inv1(SCNHANDLE text, int MaxContents, /** * Declare inventory 2's parameters. */ - -void dec_inv2(SCNHANDLE text, int MaxContents, +static void DecInv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight, int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) { @@ -492,77 +895,104 @@ void dec_inv2(SCNHANDLE text, int MaxContents, /** * Declare the bits that the inventory windows are constructed from. */ - -void dec_invw(SCNHANDLE hf) { +static void DecInvW(SCNHANDLE hf) { setInvWinParts(hf); } /** - * Declare lead actor. - * - the actor's id, walk and stand reels for all the regular scales, - * and the tag text. + * DeclareLanguage */ +static void DeclareLanguage(int languageId, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) { + LanguageFacts(languageId, hDescription, hFlagFilm); +} -void dec_lead(uint32 id, SCNHANDLE *rp, SCNHANDLE text) { - PMACTOR pActor; // Moving actor structure +/** + * 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 - Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged - setleadid(id); // Establish this as the lead - SetMover(id); // Establish as a moving actor + if (TinselV2) { + // Tinsel 2 only specifies the lead actor Id + SetLeadId(id); + RegisterMover(id); - pActor = GetMover(id); // Get moving actor structure - assert(pActor); + } else { - // 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 (j = 0; j < 4; ++j) - pActor->TalkReels[i][j] = *rp++; - } + 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) { - pActor->WalkReels[i][j] = pActor->WalkReels[4][j]; - pActor->StandReels[i][j] = pActor->StandReels[2][j]; - pActor->TalkReels[i][j] = pActor->TalkReels[4][j]; + 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]; + } } } } /** - * Declare the text font. + * DecScale("actor", scale, 12*"reel") + * Define an actor's walk and stand reels for a scale. */ - -void dec_tagfont(SCNHANDLE hf) { - TagFontHandle(hf); // Store the font handle +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 +} -void dec_talkfont(SCNHANDLE hf) { - TalkFontHandle(hf); // Store the font handle +/** + * Declare the text font. + */ +static void DecTalkFont(SCNHANDLE hf) { + SetTalkFontHandle(hf); // Store the font handle } /** * Remove an icon from the conversation window. */ - -void delicon(int icon) { +static void DelIcon(int icon) { RemFromInventory(INV_CONV, icon); } /** * Delete the object from inventory 1 or 2. */ - -void delinv(int object) { +static void DelInv(int object) { if (!RemFromInventory(INV_1, object)) // Remove from inventory 1... RemFromInventory(INV_2, object); // ...or 2 (whichever) @@ -570,18 +1000,119 @@ void delinv(int object) { } /** - * enablef1 + * DelTopic */ +static void DelTopic(int icon) { + RemFromInventory(INV_CONV, icon); +} -void enablef1(void) { - bEnableF1 = true; +/** + * DimMusic + */ +static void DimMusic(void) { + _vm->_pcmMusic->dim(true); } /** - * fademidi(in/out) + * 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(void) { + HoldItem(NOOBJECT, false); + + ClearInventory(INV_1); + ClearInventory(INV_2); +} + +/** + * EnableMenu + */ +static void EnableMenu(void) { + 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); -void fademidi(CORO_PARAM, int inout) { + /* + * 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(void) { + FadeInMedium(NULL); +} + +/** + * FadeOut + */ +static void FadeOut(void) { + FadeOutMedium(NULL); +} + +/** + * FadeMidi(in/out) + */ +static void FadeMidi(CORO_PARAM, int inout) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); @@ -595,82 +1126,159 @@ void fademidi(CORO_PARAM, int inout) { } /** - * Guess what. + * Freeze the cursor, or not. */ +static void FreezeCursor(bool bFreeze) { + DoFreezeCursor(bFreeze); +} -int getinvlimit(int invno) { +/** + * Guess what. + */ +static int GetInvLimit(int invno) { return InvGetLimit(invno); } /** + * Ghost + */ +static void Ghost(int actor, int tColour, int tPalOffset) { + SetSysVar(ISV_GHOST_ACTOR, actor); + SetSysVar(ISV_GHOST_COLOUR, tColour); + 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. */ -bool hasrestarted(void) { +static bool HasRestarted(void) { return bHasRestarted; } /** - * Returns which object is currently held. + * See if an object is in the inventory. */ +int Have(int object) { + return (InventoryPos(object) != NOOBJECT); +} -int heldobject(void) { +/** + * Returns which object is currently held. + */ +static int HeldObject(void) { return WhichItemHeld(); } /** - * Removes a player from the screen, probably when he's about to be - * replaced by an animation. - * - * Not believed to work anymore! (hide() is not used). + * Hides the specified actor */ +static void HideActorFn(CORO_PARAM, int ano) { + HideActor(coroParam, ano); +} -void hide(int actor) { - HideActor(actor); +/** + * 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)); } /** - * hookscene(scene, entrance, transition) + * Hold the specified object. */ +static void Hold(int object) { + HoldItem(object, false); +} -void hookscene(SCNHANDLE scene, int entrance, int transition) { +/** + * HookScene(scene, entrance, transition) + */ +void HookScene(SCNHANDLE scene, int entrance, int transition) { SetHookScene(scene, entrance, transition); } /** - * idletime + * IdleTime */ +static int IdleTime(void) { + // If control is off, system is not idle + if (!ControlIsOn()) { + // Player doesn't currently have control + ResetIdleTime(); -int idletime(void) { - uint32 x; + return 0; + } else { + // Player has control - return time since last event + int x = getUserEventTime() / ONE_SECOND; - x = getUserEventTime() / ONE_SECOND; - - if (!TestToken(TOKEN_CONTROL)) - resetidletime(); + return x; + } +} - return (int)x; +/** + * Set flag if InstantScroll(on), reset if InstantScroll(off) + */ +void InstantScroll(int onoff) { + bInstantScroll = (onoff != 0); } /** * invdepict */ -void invdepict(int object, SCNHANDLE hFilm) { - invObjectFilm(object, hFilm); +static void InvDepict(int object, SCNHANDLE hFilm) { + SetObjectFilm(object, hFilm); } /** * See if an object is in the inventory. */ -int ininventory(int object) { +int InInventory(int object) { return (InventoryPos(object) != INV_NOICON); } /** * Open an inventory. */ -void inventory(int invno, bool escOn, int myescEvent) { +static void Inventory(int invno, bool escOn, int myEscape) { // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; assert((invno == INV_1 || invno == INV_2)); // Trying to open illegal inventory @@ -679,9 +1287,16 @@ void inventory(int invno, bool escOn, int myescEvent) { } /** + * Alter inventory object's icon. + */ +static void InvPlay(int object, SCNHANDLE hFilm) { + SetObjectFilm(object, hFilm); +} + +/** * See if an object is in the inventory. */ -int inwhichinv(int object) { +static int InWhichInv(int object) { if (WhichItemHeld() == object) return 0; @@ -697,45 +1312,59 @@ int inwhichinv(int object) { /** * Kill an actor. */ -void killactor(int actor) { +static void KillActor(int actor) { DisableActor(actor); } /** * Turn a blocking polygon off. */ -void killblock(int block) { +static void KillBlock(int block) { DisableBlock(block); } /** * Turn an exit off. */ -void killexit(int exit) { +static void KillExit(int exit) { DisableExit(exit); } /** * Turn a tag off. */ -void killtag(int tagno) { - DisableTag(tagno); +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. */ -int ltoffset(int lort) { +static int LToffset(int lort) { int Loffset, Toffset; PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); - return (lort == LEFTOFFSET) ? Loffset : Toffset; + return (lort == SCREENXPOS) ? Loffset : Toffset; } /** * Set new cursor position. */ -void movecursor(int x, int y) { +static void MoveCursor(int x, int y) { SetCursorXY(x, y); controlX = x; // Save these values so that @@ -743,29 +1372,45 @@ void movecursor(int x, int y) { } /** + * 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) { +void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); -#ifdef BODGE - if (!ValidHandle(scene)) { - scene = GetSceneHandle(); - entrance = 1; + if (TinselV2) { + if (MoviePlaying()) { + AbortMovie(); + CORO_SLEEP(2); + } } - assert(scene); // Non-existant first scene! -#endif SetNewScene(scene, entrance, transition); -#if 1 // Prevent tags and cursor re-appearing - GetControl(CONTROL_STARTOFF); -#endif + if (TinselV2) + ControlStartOff(); + else + GetControl(CONTROL_STARTOFF); // Prevent code subsequent to this call running before scene changes if (g_scheduler->getCurrentPID() != PID_MASTER_SCR) @@ -776,47 +1421,78 @@ void newscene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) { /** * Disable dynamic blocking for current scene. */ -void noblocking(void) { - bNoBlocking = true; +static void NoBlocking(void) { + SetNoBlocking(true); } /** * Define a no-scroll boundary for the current scene. */ -void noscroll(int x1, int y1, int x2, int y2) { +static void NoScroll(int x1, int y1, int x2, int y2) { SetNoScroll(x1, y1, x2, y2); } /** * Hold the specified object. */ -void objectheld(int object) { +static void ObjectHeld(int object) { HoldItem(object); } /** * Set the top left offset of the screen. */ -void offset(int x, int y) { +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. */ -void play(CORO_PARAM, SCNHANDLE film, int x, int y, int compit, int actorid, bool splay, int sfact, - bool escOn, int myescEvent, bool bTop) { +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); - assert(film != 0); // play(): Trying to play NULL film + + // Don't do CDPlay() for now if already escaped + if (bEscapedCdPlay) { + bEscapedCdPlay = false; + return; + } // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; // If this actor is dead, call a stop to the calling process @@ -825,24 +1501,75 @@ void play(CORO_PARAM, SCNHANDLE film, int x, int y, int compit, int actorid, boo // 7/4/95 if (!escOn) - myescEvent = GetEscEvents(); + myEscape = GetEscEvents(); if (compit == 1) { // Play to completion before returning - CORO_INVOKE_ARGS(playFilmc, (CORO_SUBCTX, film, x, y, actorid, splay, sfact, escOn, myescEvent, bTop)); + 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. - playFilm(film, x, y, actorid, splay, sfact, escOn, myescEvent, bTop); + 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 (bEscapedCdPlay) { + 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. */ -void playmidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { +static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { // FIXME: This is a workaround for the FIXME below if (GetMidiVolume() == 0) return; @@ -877,9 +1604,48 @@ void playmidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) { } /** + * 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 (MoviePlaying()) + CORO_SLEEP(1); + + // Play the movie + CORO_INVOKE_2(PlayBMV, hFileStem, myEscape); + + CORO_END_CODE; +} + +/** + * Play some music + */ +static void PlayMusic(int tune) { + _vm->_pcmMusic->startPlay(tune); +} + +/** * Play a sample. + * Tinsel 1 version */ -void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEvent) { +static void PlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) { CORO_BEGIN_CONTEXT; Audio::SoundHandle handle; CORO_END_CONTEXT(_ctx); @@ -890,7 +1656,7 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven return; // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) { + if (escOn && myEscape != GetEscEvents()) { _vm->_sound->stopAllSamples(); // Stop any currently playing sample return; } @@ -898,10 +1664,10 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven if (volSound != 0 && _vm->_sound->sampleExists(sample)) { _vm->_sound->playSample(sample, Audio::Mixer::kSFXSoundType, &_ctx->handle); - if (complete) { + if (bComplete) { while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) { // Abort if escapable and ESCAPE is pressed - if (escOn && myescEvent != GetEscEvents()) { + if (escOn && myEscape != GetEscEvents()) { _vm->_mixer->stopHandle(_ctx->handle); break; } @@ -917,28 +1683,153 @@ void playsample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEven } /** - * Play a sample. + * Play a sample + * Tinsel 2 version */ -void tryplaysample(CORO_PARAM, int sample, bool complete, bool escOn, int myescEvent) { +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); - // Don't do it if it's not appropriate - if (_vm->_sound->sampleIsPlaying()) { - // return, but prevent Glitter lock-up - CORO_SLEEP(1); + + _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 (volSound != 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_INVOKE_ARGS(playsample, (CORO_SUBCTX, sample, complete, escOn, myescEvent)); 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. */ -void preparescene(SCNHANDLE scene) { +static void PrepareScene(SCNHANDLE scene) { #ifdef BODGE if (!ValidHandle(scene)) return; @@ -947,11 +1838,11 @@ void preparescene(SCNHANDLE scene) { /** * Print the given text at the given place for the given time. - * - * Print(....., h) -> hold = 1 (not used) - * Print(....., s) -> hold = 2 (sustain) */ -void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool escOn, int myescEvent) { +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; @@ -969,37 +1860,55 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es _ctx->bSample = false; // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; - // Kick off the voice sample - if (volVoice != 0 && _vm->_sound->sampleExists(text)) { - _vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle); - _ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle); + if (!TinselV2) { + // Kick off the voice sample + if (volVoice != 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 - LoadStringRes(text, tBufferAddr(), TBUFSZ); bJapDoPrintText = false; if (time == 0) { // This is a 'talky' print - _ctx->time = TextTime(tBufferAddr()); + _ctx->time = TextTime(TextBufferAddr()); // Cut short-able if sustain was not set - _ctx->myleftEvent = (hold == 2) ? 0 : GetLeftEvents(); + _ctx->myleftEvent = bSustain ? 0 : GetLeftEvents(); } else { _ctx->time = time * ONE_SECOND; - _ctx->myleftEvent = 0; + _ctx->myleftEvent = (TinselV2 && !bSustain) ? GetLeftEvents() : 0; if (isJapanMode()) bJapDoPrintText = true; } // Print the text - if (bJapDoPrintText || (!isJapanMode() && (bSubtitles || !_ctx->bSample))) { + if (TinselV2) { + int Loffset, Toffset; + PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); + _ctx->pText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), + TextBufferAddr(), 0, x - Loffset, y - Toffset, GetTagFontHandle(), + TXT_CENTRE, 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() && (bSubtitles || !_ctx->bSample))) { int Loffset, Toffset; // Screen position PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); - _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), - 0, x - Loffset, y - Toffset, hTalkFontHandle(), TXT_CENTRE); + _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), + 0, x - Loffset, y - Toffset, + TinselV2 ? GetTagFontHandle() : GetTalkFontHandle(), TXT_CENTRE); assert(_ctx->pText); // string produced NULL text if (IsTopWindow()) MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT); @@ -1009,14 +1918,14 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es */ int shift; shift = MultiRightmost(_ctx->pText) + 2; - if (shift >= BackgroundWidth()) // Not off right - MultiMoveRelXY(_ctx->pText, BackgroundWidth() - shift, 0); + 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 > BackgroundHeight()) // Not off bottom - MultiMoveRelXY(_ctx->pText, 0, BackgroundHeight() - shift); + if (shift > BgHeight()) // Not off bottom + MultiMoveRelXY(_ctx->pText, 0, BgHeight() - shift); } // Give up if nothing printed and no sample @@ -1024,37 +1933,50 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es return; // Leave it up until time runs out or whatever - _ctx->timeout = SAMPLETIMEOUT; - do { - CORO_SLEEP(1); + if (TinselV2) { + 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 && myescEvent != GetEscEvents()) - || (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents()) - || (_ctx->bSample && --_ctx->timeout <= 0)) - break; + // Cancelled? + if ( (myEscape && myEscape != GetEscEvents()) + || (!bSustain && LeftEventChange(_ctx->myleftEvent))) + break; - if (_ctx->bSample) { - // Wait for sample to end whether or not - if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) { - if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) { - // No text or speed modification - just depends on sample - break; - } else { - // Must wait for time - _ctx->bSample = false; + } 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 || speedText == 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; } - } else { - // No sample - just depends on time - if (_ctx->time-- <= 0) - break; - } - } while (1); + } while (1); + } // Delete the text if (_ctx->pText != NULL) @@ -1065,84 +1987,228 @@ void print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, int hold, bool es } -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); +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. */ -void printobj(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, const int event) { +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 + assert(pinvo != 0); // PrintObj() may only be called from an object code block + _ctx->myEscape = myEscape; - if (text == (SCNHANDLE)-1) { // 'OFF' - NotPointedRunning = true; + if (hText == (SCNHANDLE)-1) { // 'OFF' + bNotPointedRunning = true; return; } - if (text == (SCNHANDLE)-2) { // 'ON' - NotPointedRunning = false; + if (hText == (SCNHANDLE)-2) { // 'ON' + 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) { - NotPointedRunning = true; // Get POINTED text to die + bNotPointedRunning = true; // Get POINTED text to die CORO_SLEEP(1); // Give it chance to - } else - NotPointedRunning = false; // There may have been an OFF without an ON + } else if (!TinselV2) + bNotPointedRunning = false; // There may have been an OFF without an ON - // Display the text and set it's Z position - if (event == POINTED || (!isJapanMode() && (bSubtitles || !_vm->_sound->sampleExists(text)))) { - int xshift; + // Make multi-ones escape + if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->myEscape) + _ctx->myEscape = GetEscEvents(); - LoadStringRes(text, tBufferAddr(), TBUFSZ); // The text string - _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), - 0, _ctx->textx, _ctx->texty, hTagFontHandle(), TXT_CENTRE); - assert(_ctx->pText); // printobj() string produced NULL text - MultiSetZPosition(_ctx->pText, Z_INV_ITEXT); + // 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; - // Don't go off the side of the screen - xshift = MultiLeftmost(_ctx->pText); - if (xshift < 0) { - MultiMoveRelXY(_ctx->pText, - xshift, 0); - _ctx->textx -= xshift; + 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; } - xshift = MultiRightmost(_ctx->pText); - if (xshift > SCREEN_WIDTH) { - MultiMoveRelXY(_ctx->pText, SCREEN_WIDTH - xshift, 0); - _ctx->textx += SCREEN_WIDTH - xshift; + + // Display the text and set it's Z position + if (event == POINTED || (!isJapanMode() && (bSubtitles || !_ctx->bSample))) { + int xshift; + + // Get the text string + if (TinselV2) + LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ); + else + LoadStringRes(hText, TextBufferAddr(), TBUFSZ); + + _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), + 0, _ctx->textx, _ctx->texty, GetTagFontHandle(), TXT_CENTRE); + 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 (bNotPointedRunning) { + // Delete the text, and wait for the all-clear + MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText); + _ctx->pText = NULL; + + while (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(nullContext, GetPlayfieldList(FIELD_STATUS), + TextBufferAddr(), 0, _ctx->textx, _ctx->texty, GetTagFontHandle(), + TXT_CENTRE, 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 || speedText == 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->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); + } } - } else - _ctx->pText = NULL; - if (event == POINTED) { - // FIXME: Is there ever an associated sound if in POINTED mode??? - assert(!_vm->_sound->sampleExists(text)); - CORO_INVOKE_ARGS(printobjPointed, (CORO_SUBCTX, text, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item)); - } else { - CORO_INVOKE_2(printobjNonPointed, text, _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); } - // Delete the text, if haven't already - if (_ctx->pText) - MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText); + // Let POINTED text back in if this is the last + if (event != POINTED) + 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) { +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); @@ -1156,11 +2222,11 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT * break; // Give way to non-POINTED-generated text - if (NotPointedRunning) { + if (bNotPointedRunning) { // Delete the text, and wait for the all-clear MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), pText); pText = NULL; - while (NotPointedRunning) + while (bNotPointedRunning) CORO_SLEEP(1); GetCursorXY(&x, &y, false); @@ -1168,10 +2234,10 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT * break; // Re-display in the same place - LoadStringRes(text, tBufferAddr(), TBUFSZ); - pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), - 0, textx, texty, hTagFontHandle(), TXT_CENTRE); - assert(pText); // printobj() string produced NULL text + LoadStringRes(text, TextBufferAddr(), TBUFSZ); + pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(), + 0, textx, texty, GetTagFontHandle(), TXT_CENTRE); + assert(pText); // PrintObj() string produced NULL text MultiSetZPosition(pText, Z_INV_ITEXT); } @@ -1184,7 +2250,7 @@ static void printobjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT * CORO_END_CODE; } -static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) { +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; @@ -1210,7 +2276,7 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p if (isJapanMode()) _ctx->ticks = JAP_TEXT_TIME; else if (pText) - _ctx->ticks = TextTime(tBufferAddr()); + _ctx->ticks = TextTime(TextBufferAddr()); else _ctx->ticks = 0; @@ -1222,7 +2288,7 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p // 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 || convHid()) + if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || ConvIsHidden()) break; if (_ctx->bSample) { @@ -1243,10 +2309,10 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p } } while (1); - NotPointedRunning = false; // Let POINTED text back in + bNotPointedRunning = false; // Let POINTED text back in if (_ctx->took_control) - control(CONTROL_ON); // Free control if we took it + Control(CONTROL_ON); // Free control if we took it _vm->_mixer->stopHandle(_ctx->handle); @@ -1256,28 +2322,38 @@ static void printobjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *p /** * Register the fact that this poly would like its tag displayed. */ -void printtag(HPOLYGON hp, SCNHANDLE text) { - assert(hp != NOPOLY); // printtag() may only be called from a polygon code block - - if (PolyTagState(hp) == TAG_OFF) { - SetPolyTagState(hp, TAG_ON); - SetPolyTagHandle(hp, text); +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); } } /** - * quitgame + * Quits the game */ -void quitgame(void) { - stopmidi(); - stopsample(); +static void QuitGame(void) { + StopMidi(); + StopSample(); _vm->quitGame(); } /** * Return a random number between optional limits. */ -int dw_random(int n1, int n2, int norpt) { +static int RandomFn(int n1, int n2, int norpt) { int i = 0; uint32 value; @@ -1290,44 +2366,71 @@ int dw_random(int n1, int n2, int norpt) { } /** - * resetidletime + * ResetIdleTime */ -void resetidletime(void) { +void ResetIdleTime(void) { resetUserEventTime(); } /** * restartgame */ -void restartgame(void) { - stopmidi(); - stopsample(); +static void RestartGame(void) { + // TODO: Tinsel 2 comments out the 2 calls, but I'm not sure that this should be done + StopMidi(); + StopSample(); bRestart = true; } /** * Restore saved scene. */ -void restore_scene(bool bFade) { - UnSuspendHook(); - PleaseRestoreScene(bFade); +static void RestoreScene(CORO_PARAM, TRANSITS transition) { + // COROUTINE + CORO_BEGIN_CONTEXT; + CORO_END_CONTEXT(_ctx); + + CORO_BEGIN_CODE(_ctx); + + if (TinselV2) { + if (MoviePlaying()) { + AbortMovie(); + CORO_SLEEP(2); + } + + CuttingScene(false); + + } else { + UnSuspendHook(); + } + + TinselRestoreScene(transition == TRANS_FADE); + + CORO_END_CODE; +} + +/** + * Resumes the last game + */ +void ResumeLastGame(void) { + RestoreGame(NewestSavedGame()); } /** - * runmode + * Returns the current run mode */ -int runmode(void) { +static int RunMode(void) { return clRunMode; } /** - * sampleplaying + * SamplePlaying */ -bool sampleplaying(bool escOn, int myescEvent) { +static bool SamplePlaying(bool escOn, int myEscape) { // escape effects introduced 14/12/95 to fix // while (sampleplaying()) pause; - if (escOn && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return false; return _vm->_sound->sampleIsPlaying(); @@ -1336,73 +2439,169 @@ bool sampleplaying(bool escOn, int myescEvent) { /** * Save current scene. */ -void save_scene(CORO_PARAM) { - PleaseSaveScene(coroParam); - SuspendHook(); +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 + * ScalingReels */ -void scalingreels(int actor, int scale, int direction, +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); + SetScalingReels(actor, scale, direction, left, right, forward, away); } /** * Return the icon that caused the CONVERSE event. */ - -int scanicon(void) { - return convIcon(); +static int ScanIcon(void) { + return GetIcon(); } /** * Scroll the screen to target co-ordinates. */ - -void scroll(CORO_PARAM, int x, int y, int iter, bool comp, bool escOn, int myescEvent) { +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 mycount; + int thisScroll; + int x, y; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - if (escOn && myescEvent != GetEscEvents()) { + + ++scrollNumber; + _ctx->x = xp; + _ctx->y = yp; + + if ((TinselV2 && bInstantScroll) || (escOn && myEscape != GetEscEvents())) { // Instant completion! - offset(x, y); + Offset(extreme, _ctx->x, _ctx->y); } else { - _ctx->mycount = ++scrollCount; + _ctx->thisScroll = scrollNumber; + if (TinselV2) + DecodeExtreme(extreme, &_ctx->x, &_ctx->y); - ScrollTo(x, y, iter); + ScrollTo(_ctx->x, _ctx->y, xIter, yIter); - if (comp) { + if (bComp) { int Loffset, Toffset; do { CORO_SLEEP(1); // If escapable and ESCAPE is pressed... - if (escOn && myescEvent != GetEscEvents()) { + if (escOn && myEscape != GetEscEvents()) { // Instant completion! - offset(x, y); + Offset(extreme, _ctx->x, _ctx->y); break; } // give up if have been superseded - if (_ctx->mycount != scrollCount) + if (_ctx->thisScroll != scrollNumber) CORO_KILL_SELF(); PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); - } while (Loffset != x || Toffset != y); + } while (Loffset != _ctx->x || Toffset != _ctx->y); + } else if (TinselV2 && myEscape) { + static SCROLL_MONITOR sm; + + // Scroll is escapable even though we're not waiting for it + sm.x = _ctx->x; + sm.y = _ctx->y; + sm.thisScroll = scrollNumber; + sm.myEscape = myEscape; + g_scheduler->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. */ -void setactor(int actor) { +static void SetActor(int actor) { EnableActor(actor); } @@ -1410,7 +2609,7 @@ void setactor(int actor) { * Turn a blocking polygon on. */ -void setblock(int blockno) { +static void SetBlock(int blockno) { EnableBlock(blockno); } @@ -1418,21 +2617,21 @@ void setblock(int blockno) { * Turn an exit on. */ -void setexit(int exitno) { +static void SetExit(int exitno) { EnableExit(exitno); } /** * Guess what. */ -void setinvlimit(int invno, int n) { +static void SetInvLimit(int invno, int n) { InvSetLimit(invno, n); } /** * Guess what. */ -void setinvsize(int invno, int MinWidth, int MinHeight, +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); } @@ -1440,7 +2639,7 @@ void setinvsize(int invno, int MinWidth, int MinHeight, /** * Guess what. */ -void setlanguage(LANGUAGE lang) { +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 @@ -1451,191 +2650,332 @@ void setlanguage(LANGUAGE lang) { /** * Set palette */ -void setpalette(SCNHANDLE hPal, bool escOn, int myescEvent) { +static void SetPalette(SCNHANDLE hPal, bool escOn, int myEscape) { // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) + 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. */ -void settag(int tagno) { - EnableTag(tagno); +static void SetTag(CORO_PARAM, int tagno) { + EnableTag(coroParam, tagno); } /** * Initialise a timer. */ -void settimer(int timerno, int start, bool up, bool frame) { - DwSetTimer(timerno, start, up != 0, frame != 0); +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. */ -void showpos(void) { +static void showpos(void) { setshowpos(); } /** * Enable display of diagnostic co-ordinates. */ -void showstring(void) { +static void showstring(void) { setshowstring(); } #endif /** + * Shows the main menu + */ +static void ShowMenu(void) { + 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. */ - -void splay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myescEvent) { +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 && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; - play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myescEvent, false); + 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); -void stand(int actor, int x, int y, SCNHANDLE film) { - PMACTOR pActor; // Moving actor structure + _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; + } - pActor = GetMover(actor); - if (pActor) { - if (pActor->MActorState == NO_MACTOR) { + if (!MoverIs(_ctx->pMover)) { // create a moving actor process - MActorProcessCreate(x, y, (actor == LEAD_ACTOR) ? LeadId() : actor, pActor); + MoverProcessCreate(x, y, (actor == LEAD_ACTOR) ? GetLeadId() : actor, _ctx->pMover); - if (film == TF_NONE) { - SetMActorStanding(pActor); + if (hFilm == TF_NONE) { + // Make sure there is an assigned actorObj + while (!_ctx->pMover->actorObj) + CORO_SLEEP(1); + + SetMoverStanding(_ctx->pMover); } else { - switch (film) { - case TF_NONE: - break; - - case TF_UP: - SetMActorDirection(pActor, AWAY); - SetMActorStanding(pActor); - break; - case TF_DOWN: - SetMActorDirection(pActor, FORWARD); - SetMActorStanding(pActor); - break; - case TF_LEFT: - SetMActorDirection(pActor, LEFTREEL); - SetMActorStanding(pActor); - break; - case TF_RIGHT: - SetMActorDirection(pActor, RIGHTREEL); - SetMActorStanding(pActor); - break; - - default: - AlterMActor(pActor, film, AR_NORMAL); - break; + // 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 (film) { + switch (hFilm) { case TF_NONE: if (x != -1 && y != -1) - MoveMActor(pActor, x, y); + PositionMover(_ctx->pMover, x, y); break; case TF_UP: - SetMActorDirection(pActor, AWAY); + SetMoverDirection(_ctx->pMover, AWAY); if (x != -1 && y != -1) - MoveMActor(pActor, x, y); - SetMActorStanding(pActor); + PositionMover(_ctx->pMover, x, y); + SetMoverStanding(_ctx->pMover); break; case TF_DOWN: - SetMActorDirection(pActor, FORWARD); + SetMoverDirection(_ctx->pMover, FORWARD); if (x != -1 && y != -1) - MoveMActor(pActor, x, y); - SetMActorStanding(pActor); + PositionMover(_ctx->pMover, x, y); + SetMoverStanding(_ctx->pMover); break; case TF_LEFT: - SetMActorDirection(pActor, LEFTREEL); + SetMoverDirection(_ctx->pMover, LEFTREEL); if (x != -1 && y != -1) - MoveMActor(pActor, x, y); - SetMActorStanding(pActor); + PositionMover(_ctx->pMover, x, y); + SetMoverStanding(_ctx->pMover); break; case TF_RIGHT: - SetMActorDirection(pActor, RIGHTREEL); + SetMoverDirection(_ctx->pMover, RIGHTREEL); if (x != -1 && y != -1) - MoveMActor(pActor, x, y); - SetMActorStanding(pActor); + PositionMover(_ctx->pMover, x, y); + SetMoverStanding(_ctx->pMover); break; default: if (x != -1 && y != -1) - MoveMActor(pActor, x, y); - AlterMActor(pActor, film, AR_NORMAL); + PositionMover(_ctx->pMover, x, y); + AlterMover(_ctx->pMover, hFilm, AR_NORMAL); break; } } } else if (actor == NULL_ACTOR) { // } else { - assert(film != 0); // Trying to play NULL film + assert(hFilm != 0); // Trying to play NULL film // Kick off the play and return. - playFilm(film, x, y, actor, false, 0, false, 0, false); + 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. */ -void standtag(int actor, HPOLYGON hp) { - SCNHANDLE film; +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 + 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 - film = getPolyFilm(hp); - getPolyNode(hp, &pnodex, &pnodey); - if (film && (actor == LEAD_ACTOR || actor == LeadId())) - stand(actor, pnodex, pnodey, 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(nullContext, actor, pnodex, pnodey, hFilm); + + } else if (hFilm && (actor == LEAD_ACTOR || actor == GetLeadId())) + Stand(nullContext, actor, pnodex, pnodey, hFilm); else - stand(actor, pnodex, pnodey, 0); + Stand(nullContext, actor, pnodex, pnodey, 0); } + /** - * Kill a moving actor's walk. + * StartGlobalProcess */ -void stop(int actor) { - PMACTOR pActor; +static void StartGlobalProcess(CORO_PARAM, uint32 procID) { + GlobalProcessEvent(coroParam, procID, STARTUP, false, 0); +} - pActor = GetMover(actor); - assert(pActor); // Trying to stop a null actor +/** + * StartProcess + */ +static void StartProcess(CORO_PARAM, uint32 procID) { + SceneProcessEvent(coroParam, procID, STARTUP, false, 0); +} - GetToken(pActor->actorToken); // Kill the walk process - pActor->stop = true; // Cause the actor to stop - FreeToken(pActor->actorToken); +/** + * Initialise a timer. + */ +static void StartTimerFn(int timerno, int start, bool up, int fs) { + StartTimer(timerno, start, up, fs); } -void stopmidi(void) { +void StopMidiFn(void) { StopMidi(); // Stop any currently playing midi } -void stopsample(void) { - _vm->_sound->stopAllSamples(); // Stop any currently playing sample +/** + * 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); + } } -void subtitles(int onoff) { +/** + * Subtitles on/off + */ +static void Subtitles(int onoff) { assert (onoff == ST_ON || onoff == ST_OFF); if (isJapanMode()) @@ -1651,271 +2991,468 @@ void subtitles(int onoff) { * Special walk. * Walk into or out of a legal path. */ -void swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, bool escOn, int myescEvent) { +static void Swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, int zOverride, bool escOn, int myEscape) { CORO_BEGIN_CONTEXT; - bool took_control; // Set if this function takes control + 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 && myescEvent != GetEscEvents()) + 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 == LeadId() || actor == LEAD_ACTOR) - _ctx->took_control = GetControl(CONTROL_OFFV2); - else - _ctx->took_control = false; - - HPOLYGON hPath; + if (actor == GetLeadId() || actor == LEAD_ACTOR) { + _ctx->bTookControl = GetControl(CONTROL_OFFV2); + if (TinselV2 && _ctx->bTookControl) + RestoreMainCursor(); + } else { + _ctx->bTookControl = false; + } - hPath = InPolygon(x1, y1, PATH); - if (hPath != NOPOLY) { - // Walking out of a path - stand(actor, x1, y1, 0); + if (TinselV2 && (x2 == -1) && (y2 == -1)) { + // First co-ordinates are the destination + x2 = x1; + y2 = y1; } 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 + // 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 - stand(actor, x2, y2, 0); // Get path's characteristics - stand(actor, x1, y1, 0); + // 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, escOn, myescEvent)); + CORO_INVOKE_ARGS(Walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, zOverride, escOn, myEscape)); // Free control if we took it - if (_ctx->took_control) - control(CONTROL_ON); + if (_ctx->bTookControl) + Control(CONTROL_ON); CORO_END_CODE; } /** - * Define a tagged actor. + * Gets a system variable */ +static int SystemVar(int varId) { + return SysVar(varId); +} -void tagactor(int actor, SCNHANDLE text, int tp) { +/** + * 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); -void FinishTalkingReel(PMACTOR pActor, int actor) { - if (pActor) { - SetMActorStanding(pActor); - AlterMActor(pActor, 0, AR_POPREEL); + if (pMover) { + SetMoverStanding(pMover); + AlterMover(pMover, 0, AR_POPREEL); } else { - setActorTalking(actor, false); - playFilm(getActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false); + SetActorTalking(actor, false); + CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false)); } + + CORO_END_CODE; } -void talk(CORO_PARAM, SCNHANDLE film, const SCNHANDLE text, int actorid, bool escOn, int myescEvent) { +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 - PMACTOR pActor; // For moving actors - int myleftEvent; + 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 && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; - _ctx->myleftEvent = GetLeftEvents(); + _ctx->myLeftEvent = GetLeftEvents(); // If this actor is dead, call a stop to the calling process - if (actorid && !actorAlive(actorid)) + if (!TinselV2 && (actorId && !actorAlive(actorId))) CORO_KILL_SELF(); - /* - * Find out which actor is talking - * and with which direction if no film supplied - */ - TFTYPE direction; - switch (film) { - case TF_NONE: - case TF_UP: - case TF_DOWN: - case TF_LEFT: - case TF_RIGHT: - _ctx->actor = LeadId(); // If no film, actor is lead actor - direction = (TFTYPE)film; - break; + 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(film); - assert(_ctx->actor); // talk() - no actor ID in the reel - direction = TF_BOGUS; - 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 == LeadId()) + 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 (volVoice != 0 && _vm->_sound->sampleExists(text)) { - _vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle); - _ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle); + if (volVoice != 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->bSample = false; + _ctx->bSamples = false; /* * Replace actor with the talk reel, saving the current one */ _ctx->pActor = GetMover(_ctx->actor); - if (_ctx->pActor) { - if (direction != TF_BOGUS) - film = GetMactorTalkReel(_ctx->pActor, direction); - AlterMActor(_ctx->pActor, film, AR_PUSHREEL); - } else { - setActorTalking(_ctx->actor, true); - setActorTalkFilm(_ctx->actor, film); - playFilm(film, -1, -1, 0, false, 0, escOn, myescEvent, false); + 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; } - _ctx->bTalkReel = true; - CORO_SLEEP(1); // Allow the play to come in - /* - * Display the text. - */ - _ctx->pText = NULL; - if (isJapanMode()) { - _ctx->ticks = JAP_TEXT_TIME; - } else if (bSubtitles || !_ctx->bSample) { - int aniX, aniY; // actor position - int xshift, yshift; - /* - * Work out where to display the text - */ - PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset); - GetActorMidTop(_ctx->actor, &aniX, &aniY); - aniY -= _ctx->Toffset; - - setTextPal(getActorTcol(_ctx->actor)); - LoadStringRes(text, tBufferAddr(), TBUFSZ); - _ctx->pText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), - 0, aniX - _ctx->Loffset, aniY, hTalkFontHandle(), TXT_CENTRE); - assert(_ctx->pText); // talk() string produced NULL text; - if (IsTopWindow()) - MultiSetZPosition(_ctx->pText, Z_TOPW_TEXT); + // Make multi-ones escape + if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->escEvents) + _ctx->escEvents = GetEscEvents(); - /* - * Set bottom of text just above the speaker's head - * But don't go off the top of the screen - */ - yshift = aniY - 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 + for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) { + if (TinselV2 && _ctx->escEvents && _ctx->escEvents != GetEscEvents()) + break; /* - * Don't go off the side of the screen + * Display the text. */ - 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); - /* - * Work out how long to talk. - * During this time, reposition the text if the screen scrolls. - */ - _ctx->ticks = TextTime(tBufferAddr()); - } + _ctx->bSample = _ctx->bSamples; + _ctx->pText = NULL; - _ctx->timeout = SAMPLETIMEOUT; - do { - // Keep text in place if scrolling - if (_ctx->pText != NULL) { - int nLoff, nToff; + if (isJapanMode()) { + _ctx->ticks = JAP_TEXT_TIME; + } else if (bSubtitles || !_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); + + SetTextPal(GetActorRGB(_ctx->actor)); + if (TinselV2) + LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ); + else { + LoadStringRes(hText, TextBufferAddr(), TBUFSZ); + + _ctx->y -= _ctx->Toffset; + } - 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; + _ctx->pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), + TextBufferAddr(), 0, _ctx->x - _ctx->Loffset, _ctx->y - _ctx->Toffset, + GetTalkFontHandle(), TXT_CENTRE); + 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()); } - CORO_SLEEP(1); - --_ctx->timeout; + if (TinselV2 && _ctx->bSample) { + // Kick off the sample now (perhaps with a delay) + if (bNoPause) + bNoPause = false; + else + CORO_SLEEP(SysVar(SV_SPEECHDELAY)); - // Abort if escapable and ESCAPE is pressed - // Abort if left click - hardwired feature for talk! - // Abort if sample times out - if ((escOn && myescEvent != GetEscEvents()) - || (_ctx->myleftEvent != GetLeftEvents()) - || (_ctx->timeout <= 0)) - break; + //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; - if (_ctx->bSample) { - // Wait for sample to end whether or not - if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) { - if (_ctx->pText == NULL || speedText == DEFTEXTSPEED) { - // No text or speed modification - just depends on sample + 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 { - // Talk reel stops at end of speech - FinishTalkingReel(_ctx->pActor, _ctx->actor); - _ctx->bTalkReel = false; - _ctx->bSample = false; } + } 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; } - } else { - // No sample - just depends on time - if (_ctx->ticks-- <= 0) - break; + + if (_ctx->bSample) { + // Wait for sample to end whether or not + if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) { + if (_ctx->pText == NULL || speedText == 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; + } + } + } 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; } - } while (1); + 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); - _vm->_mixer->stopHandle(_ctx->handle); - if (_ctx->bTalkReel) - FinishTalkingReel(_ctx->pActor, _ctx->actor); + + 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) - control(CONTROL_ON); + if (_ctx->bTookControl) { + if (TinselV2) ControlOn(); else Control(CONTROL_ON); + } if (_ctx->bTookTags) EnableTags(); @@ -1923,135 +3460,238 @@ void talk(CORO_PARAM, SCNHANDLE film, const SCNHANDLE text, int actorid, bool es } /** - * talkat(actor, x, y, text) + * TalkAt(actor, x, y, text) */ -void talkat(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myescEvent) { +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 && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; if (!isJapanMode() && (bSubtitles || !_vm->_sound->sampleExists(text))) - setTextPal(getActorTcol(actor)); + SetTextPal(GetActorRGB(actor)); } - print(coroParam, x, y, text, 0, 0, escOn, myescEvent); + Print(coroParam, x, y, text, 0, false, escOn, myEscape); } /** - * talkats(actor, x, y, text, sustain) + * TalkAtS(actor, x, y, text, sustain) */ -void talkats(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myescEvent) { +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 && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; if (!isJapanMode()) - setTextPal(getActorTcol(actor)); + SetTextPal(GetActorRGB(actor)); } - print(coroParam, x, y, text, 0, sustain, escOn, myescEvent); + Print(coroParam, x, y, text, 0, sustain == 2, escOn, myEscape); } /** * Set talk font's palette entry. */ -void talkattr(int r1, int g1, int b1, bool escOn, int myescEvent) { +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 && myescEvent != GetEscEvents()) + 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(RGB(r1, g1, b1)); + SetTextPal(RGB(r1, g1, b1)); +} + +/** + * TalkPaletteIndex + */ +static void TalkPaletteIndex(unsigned index) { + assert(index); + + SetTalkTextOffset(index); +} + +/** + * Set talk font's palette entry. + */ +static void TalkRGB(COLORREF colour, int myescEvent) { + // Don't do it if it's not wanted + if (myescEvent && myescEvent != GetEscEvents()) + return; + + SetTextPal(colour); +} + +/** + * 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. */ -int timer(int timerno) { +static int TimerFn(int timerno) { return Timer(timerno); } /** + * Return the icon that caused the CONVERSE event. + */ +int Topic(void) { + return GetIcon(); +} + +/** * topplay(film, x, y, actor, hold, complete) */ -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 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' */ -void topwindow(int bpos) { - assert(bpos == TW_START || bpos == TW_END); +static void TopWindow(int bpos) { + bool isStart = (TinselV2 && (bpos != 0)) || (!TinselV2 && (bpos == TW_START)); - switch (bpos) { - case TW_END: - KillInventory(); - break; + KillInventory(); - case TW_START: - KillInventory(); - PopUpConf(TOPWIN); - break; + 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(void) { + _vm->_pcmMusic->unDim(true); } /** * unhookscene */ -void unhookscene(void) { +static void UnHookSceneFn(void) { UnHookScene(); } /** * Un-define an actor as tagged. */ -void untagactor(int actor) { +static void UnTagActorFn(int actor) { UnTagActor(actor); } /** * vibrate */ -void vibrate(void) { +static void Vibrate(void) { } /** * waitframe(int actor, int frameNumber) */ -void waitframe(CORO_PARAM, int actor, int frameNumber, bool escOn, int myescEvent) { +static void WaitFrame(CORO_PARAM, int actor, int frameNumber, bool escOn, int myEscape) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); - // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) - return; - while (getActorSteps(actor) < frameNumber) { - CORO_SLEEP(1); - - // Abort if escapable and ESCAPE is pressed - if (escOn && myescEvent != GetEscEvents()) + 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. */ -void waitkey(CORO_PARAM, bool escOn, int myescEvent) { +static void WaitKey(CORO_PARAM, bool escOn, int myEscape) { CORO_BEGIN_CONTEXT; int startEvent; int startX, startY; @@ -2060,35 +3700,38 @@ void waitkey(CORO_PARAM, bool escOn, int myescEvent) { CORO_BEGIN_CODE(_ctx); // Don't do it if it's not wanted - if (escOn && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; - while (1) { + for (;;) { _ctx->startEvent = getUserEvents(); - // Store cursor position - while (!GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false)) - CORO_SLEEP(1); + if (!TinselV2) { + // 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 (!TinselV2) { + int curX, curY; + GetCursorXY(&curX, &curY, false); // Store cursor position + if (curX != _ctx->startX || curY != _ctx->startY) + break; + } - int curX, curY; - GetCursorXY(&curX, &curY, false); // Store cursor position - if (curX != _ctx->startX || curY != _ctx->startY) - break; - - if (IsConfWindow()) + if (MenuActive()) break; } - if (!IsConfWindow()) + if (!MenuActive()) return; - + do { CORO_SLEEP(1); - } while (IsConfWindow()); + } while (MenuActive()); CORO_SLEEP(ONE_SECOND / 2); // Let it die down } @@ -2096,16 +3739,38 @@ void waitkey(CORO_PARAM, bool escOn, int myescEvent) { } /** + * 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. */ -void waittime(CORO_PARAM, int time, bool frame, bool escOn, int myescEvent) { +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 && myescEvent != GetEscEvents()) + if (escOn && myEscape != GetEscEvents()) return; if (!frame) @@ -2116,51 +3781,97 @@ void waittime(CORO_PARAM, int time, bool frame, bool escOn, int myescEvent) { CORO_SLEEP(1); // Abort if escapable and ESCAPE is pressed - if (escOn && myescEvent != GetEscEvents()) + 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 film, int hold, bool igPath, bool escOn, int myescEvent) { +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); - PMACTOR pActor = GetMover(actor); - assert(pActor); // Can't walk a non-moving actor + 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()) { - stand(actor, x, y, 0); + if (TinselV2) + StopMover(pMover); + CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0)); return; } - assert(pActor->hCpath != NOPOLY); // moving actor not in path + if (TinselV2) { + if (MoverHidden(pMover)) + return; - GetToken(pActor->actorToken); - SetActorDest(pActor, x, y, igPath, film); - DontScrollCursor(); + // 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); - if (hold == 2) { - ; + // Die if superceded + if (_ctx->thisWalk != GetWalkNumber(pMover)) + CORO_KILL_SELF(); + } + } } else { - while (MAmoving(pActor)) { - CORO_SLEEP(1); - // Straight there if escaped - if (escOn && myescEvent != GetEscEvents()) { - stand(actor, x, y, 0); - FreeToken(pActor->actorToken); - return; + 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); } - FreeToken(pActor->actorToken); + CORO_END_CODE; } @@ -2168,56 +3879,65 @@ void walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool ig * Set a moving actor off on a walk. * Wait to see if its aborted or completed. */ -void walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myescEvent, bool &retVal) { +static void Walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myEscape, bool &retVal) { // COROUTINE CORO_BEGIN_CONTEXT; - int ticket; + int thisWalk; CORO_END_CONTEXT(_ctx); - PMACTOR pActor = GetMover(actor); - assert(pActor); // Can't walk a non-moving actor + 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()) { - stand(actor, x, y, 0); + if (escOn && myEscape != GetEscEvents()) { + CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0)); retVal = true; return; } - CORO_SLEEP(ONE_SECOND); - - assert(pActor->hCpath != NOPOLY); // moving actor not in path - - // Briefly aquire token to kill off any other normal walk - GetToken(pActor->actorToken); - FreeToken(pActor->actorToken); - - SetActorDest(pActor, x, y, false, film); - DontScrollCursor(); - - _ctx->ticket = GetActorTicket(pActor); - - while (MAmoving(pActor)) { - CORO_SLEEP(1); + if (TinselV2) { + if (MoverHidden(pMover) || !MoverIs(pMover)) { + retVal = false; + return; + } + assert(pMover->hCpath != NOPOLY); // moving actor not in path - if (_ctx->ticket != GetActorTicket(pActor)) { + // 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 && myescEvent != GetEscEvents()) { - stand(actor, x, y, 0); + if (escOn && myEscape != GetEscEvents()) { + CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0)); retVal = true; return; } + + CORO_SLEEP(1); } int endx, endy; - GetMActorPosition(pActor, &endx, &endy); - retVal = (_ctx->ticket == GetActorTicket(pActor) && endx == x && endy == y); + GetMoverPosition(pMover, &endx, &endy); + retVal = (_ctx->thisWalk == GetWalkNumber(pMover) && endx == x && endy == y); CORO_END_CODE; } @@ -2225,10 +3945,15 @@ void walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int /** * Declare a moving actor. */ -void walkingactor(uint32 id, SCNHANDLE *rp) { - PMACTOR pActor; // Moving actor structure +static void WalkingActor(uint32 id, SCNHANDLE *rp = NULL) { + PMOVER pActor; // Moving actor structure - SetMover(id); // Establish as a moving actor + if (TinselVersion == TINSEL_V2) { + RegisterMover(id); + return; + } + + RegisterMover(id); // Establish as a moving actor pActor = GetMover(id); assert(pActor); @@ -2236,16 +3961,16 @@ void walkingactor(uint32 id, SCNHANDLE *rp) { int i, j; for (i = 0; i < 5; ++i) { for (j = 0; j < 4; ++j) - pActor->WalkReels[i][j] = *rp++; + pActor->walkReels[i][j] = *rp++; for (j = 0; j < 4; ++j) - pActor->StandReels[i][j] = *rp++; + 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]; + pActor->walkReels[i][j] = pActor->walkReels[4][j]; + pActor->standReels[i][j] = pActor->standReels[2][j]; } } } @@ -2254,60 +3979,75 @@ void walkingactor(uint32 id, SCNHANDLE *rp) { * Walk a moving actor towards the polygon's tag, but return when the * actor enters the polygon. */ -void walkpoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myescEvent) { +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); - PMACTOR pActor = GetMover(actor); - assert(pActor); // Can't walk a non-moving actor + 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); - int aniX, aniY; // cursor/actor position - int pnodex, pnodey; - - assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block - // Straight there if escaped - if (escOn && myescEvent != GetEscEvents()) { - standtag(actor, hp); + if (escOn && myEscape != GetEscEvents()) { + StandTag(actor, hp); return; } - GetToken(pActor->actorToken); - getPolyNode(hp, &pnodex, &pnodey); - SetActorDest(pActor, pnodex, pnodey, false, film); + 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(); - do { + while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) { CORO_SLEEP(1); - if (escOn && myescEvent != GetEscEvents()) { + if (escOn && myEscape != GetEscEvents()) { // Straight there if escaped - standtag(actor, hp); - FreeToken(pActor->actorToken); + StandTag(actor, hp); + if (!TinselV2) + FreeToken(pMover->actorToken); return; } - GetMActorPosition(pActor, &aniX, &aniY); - } while (!MActorIsInPolygon(pActor, hp) && MAmoving(pActor)); + // Die if superceded + if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover))) + CORO_KILL_SELF(); + } - FreeToken(pActor->actorToken); + if (!TinselV2) + FreeToken(pMover->actorToken); CORO_END_CODE; } /** - * walktag(actor, reel, hold) + * WalkTag(actor, reel, hold) */ -void walktag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myescEvent) { +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); - PMACTOR pActor = GetMover(actor); - assert(pActor); // Can't walk a non-moving actor + PMOVER pMover = GetMover(actor); + assert(pMover); // Can't walk a non-moving actor CORO_BEGIN_CODE(_ctx); @@ -2316,67 +4056,102 @@ void walktag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block // Straight there if escaped - if (escOn && myescEvent != GetEscEvents()) { - standtag(actor, hp); + if (escOn && myEscape != GetEscEvents()) { + StandTag(actor, hp); return; } - GetToken(pActor->actorToken); - getPolyNode(hp, &pnodex, &pnodey); - SetActorDest(pActor, pnodex, pnodey, false, film); - DoScrollCursor(); + if (!TinselV2) + GetToken(pMover->actorToken); + else { + if (MoverHidden(pMover)) + return; + } - while (MAmoving(pActor)) { - CORO_SLEEP(1); + GetPolyNode(hp, &pnodex, &pnodey); + + _ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film); + DoScrollCursor(); - if (escOn && myescEvent != GetEscEvents()) { + while (MoverMoving(pMover)) { + if (escOn && myEscape != GetEscEvents()) { // Straight there if escaped - standtag(actor, hp); - FreeToken(pActor->actorToken); + 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); + SCNHANDLE pFilm = GetPolyFilm(hp); - switch (pfilm) { + switch (pFilm) { case TF_NONE: break; case TF_UP: - SetMActorDirection(pActor, AWAY); - SetMActorStanding(pActor); + SetMoverDirection(pMover, AWAY); + SetMoverStanding(pMover); break; case TF_DOWN: - SetMActorDirection(pActor, FORWARD); - SetMActorStanding(pActor); + SetMoverDirection(pMover, FORWARD); + SetMoverStanding(pMover); break; case TF_LEFT: - SetMActorDirection(pActor, LEFTREEL); - SetMActorStanding(pActor); + SetMoverDirection(pMover, LEFTREEL); + SetMoverStanding(pMover); break; case TF_RIGHT: - SetMActorDirection(pActor, RIGHTREEL); - SetMActorStanding(pActor); + SetMoverDirection(pMover, RIGHTREEL); + SetMoverStanding(pMover); break; default: - if (actor == LEAD_ACTOR || actor == LeadId()) - AlterMActor(pActor, pfilm, AR_NORMAL); + if (actor == LEAD_ACTOR || actor == GetLeadId()) + AlterMover(pMover, pFilm, AR_NORMAL); else - SetMActorStanding(pActor); + SetMoverStanding(pMover); break; } - FreeToken(pActor->actorToken); + if (!TinselV2) + FreeToken(pMover->actorToken); + CORO_END_CODE; } /** + * Returns the X co-ordinateof lead actor's last walk. + */ +int WalkXPos(void) { + return GetLastLeadXdest(); +} + +/** + * Returns the Y co-ordinateof lead actor's last walk. + */ +int WalkYPos(void) { + return GetLastLeadYdest(); +} + +/** + * Return which is the current CD, counting from 1. + */ +int WhichCd(void) { + return GetCurrentCD(); +} + +/** * whichinventory */ -int whichinventory(void) { +int WhichInventory(void) { return WhichInventoryOpen(); } @@ -2395,544 +4170,1350 @@ int whichinventory(void) { * @param pp Top of parameter stack */ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState) { - debug(7, "CallLibraryRoutine op %d (escOn %d, myescEvent %d)", operand, pic->escOn, pic->myescEvent); - switch (operand) { + int libCode = TinselV2 ? DW2_CODES[operand] : DW1_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]); + 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: - pp[0] = actordirection(pp[0]); + // 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 error("actorref isn't a real function!"); + case ACTORRGB: + // DW2 only + pp -= 1; // 2 parameters + ActorRGB(pp[0], pp[1]); + return -2; + case ACTORSCALE: - pp[0] = actorscale(pp[0]); + // Common to both DW1 & DW2 + pp[0] = ActorScale(pp[0]); return 0; case ACTORSON: - actorson(); + // DW1 only + ActorsOn(); return 0; case ACTORXPOS: - pp[0] = actorpos(ACTORXPOS, pp[0]); + // Common to both DW1 & DW2 + pp[0] = ActorPos(ACTORXPOS, pp[0]); return 0; case ACTORYPOS: - pp[0] = actorpos(ACTORYPOS, pp[0]); + // Common to both DW1 & DW2 + pp[0] = ActorPos(ACTORYPOS, pp[0]); return 0; - case ADDICON: - addicon(pp[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: - addinv(INV_1, pp[0]); + // Common to both DW1 & DW2 + AddInv(INV_1, pp[0]); return -1; case ADDINV2: - addinv(INV_2, pp[0]); + // Common to both DW1 & DW2 + AddInv(INV_2, pp[0]); return -1; case ADDOPENINV: - addinv(INV_OPEN, pp[0]); + // 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)); + AuxScale(pp[0], pp[1], (SCNHANDLE *)(pp+2)); return -14; case BACKGROUND: - background(pp[0]); + // 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: - camera(pp[0]); + // 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: - pp -= 1; // 2 parameters - cdload(pp[0], pp[1]); + // 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: - clearhookscene(); + // Common to both DW1 & DW2 + ClearHookScene(); return 0; case CLOSEINVENTORY: - closeinventory(); + // Common to both DW1 & DW2 + CloseInventory(); return 0; case CONTROL: - control(pp[0]); + // Common to both DW1 & DW2 + Control(pp[0]); return -1; case CONVERSATION: - conversation(pp[0], pic->hpoly, pic->escOn, pic->myescEvent); + // 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 CONVICON: - convicon(pp[0]); + case CURSOR: + // DW2 only + Cursor(pp[0]); return -1; case CURSORXPOS: - pp[0] = cursorpos(CURSORXPOS); + // Common to both DW1 & DW2 + pp[0] = CursorPos(CURSORXPOS); return 0; case CURSORYPOS: - pp[0] = cursorpos(CURSORYPOS); + // Common to both DW1 & DW2 + pp[0] = CursorPos(CURSORYPOS); return 0; case CUTSCENE: + // DW1 only error("cutscene isn't a real function!"); - case DEC_CONVW: + case DECCONVW: + // Common to both DW1 & DW2 pp -= 7; // 8 parameters - dec_convw(pp[0], pp[1], pp[2], pp[3], + DecConvW(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pp[7]); return -8; - case DEC_CSTRINGS: + case DECCSTRINGS: + // DW1 only pp -= 19; // 20 parameters - dec_cstrings((SCNHANDLE *)pp); + DecCStrings((SCNHANDLE *)pp); return -20; - case DEC_CURSOR: - dec_cursor(pp[0]); + case DECCURSOR: + // Common to both DW1 & DW2 + DecCursor(pp[0]); return -1; - case DEC_FLAGS: - dec_flags(pp[0]); + case DECFLAGS: + // Common to both DW1 & DW2 + if (TinselV2) + error("DecFlags() is obsolete!"); + + DecFlags(pp[0]); return -1; - case DEC_INV1: + case DECINV1: + // Common to both DW1 & DW2 pp -= 7; // 8 parameters - dec_inv1(pp[0], pp[1], pp[2], pp[3], + DecInv1(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pp[7]); return -8; - case DEC_INV2: + case DECINV2: + // Common to both DW1 & DW2 pp -= 7; // 8 parameters - dec_inv2(pp[0], pp[1], pp[2], pp[3], + DecInv2(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pp[7]); return -8; - case DEC_INVW: - dec_invw(pp[0]); + case DECINVW: + // Common to both DW1 & DW2 + DecInvW(pp[0]); return -1; - case DEC_LEAD: - pp -= 61; // 62 parameters - dec_lead(pp[0], (SCNHANDLE *)&pp[1], pp[61]); - return -62; + 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 DEC_TAGFONT: - dec_tagfont(pp[0]); + case DECTAGFONT: + // Common to both DW1 & DW2 + DecTagFont(pp[0]); return -1; - case DEC_TALKFONT: - dec_talkfont(pp[0]); + case DECTALKFONT: + // Common to both DW1 & DW2 + DecTalkFont(pp[0]); return -1; case DELICON: - delicon(pp[0]); + // DW1 only + DelIcon(pp[0]); return -1; case DELINV: - delinv(pp[0]); + // 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)\n", pp[0]); + case EFFECTACTOR: - assert(pic->event == ENTER || pic->event == LEAVE); // effectactor() must be from effect poly code + // Common to both DW1 & DW2 + assert(pic->event == WALKIN || pic->event == WALKOUT); // effectactor() must be from effect poly code - pp[0] = pic->actorid; + pp[0] = pic->idActor; return 0; - case ENABLEF1: - enablef1(); + 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: - pp[0] = pic->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: - fademidi(coroParam, pp[0]); + // 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: - pp[0] = getinvlimit(pp[0]); + // 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: - pp[0] = 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: - pp[0] = heldobject(); + // Common to both DW1 & DW2 + pp[0] = HeldObject(); return 0; - case HIDE: - hide(pp[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]); + HookScene(pp[0], pp[1], pp[2]); return -3; case IDLETIME: - pp[0] = idletime(); + // Common to both DW1 & DW2 + pp[0] = IdleTime(); return 0; case ININVENTORY: - pp[0] = ininventory(pp[0]); + // 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]); + InvDepict(pp[0], pp[1]); return -2; case INVENTORY: - inventory(pp[0], pic->escOn, pic->myescEvent); + // 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: - pp[0] = inwhichinv(pp[0]); + // Common to both DW1 & DW2 + pp[0] = InWhichInv(pp[0]); return 0; // using return value case KILLACTOR: - killactor(pp[0]); + // DW1 only + if (TinselV2) + error("KillActor() was not expected to be required!"); + + KillActor(pp[0]); return -1; case KILLBLOCK: - killblock(pp[0]); + // DW1 only + KillBlock(pp[0]); return -1; case KILLEXIT: - killexit(pp[0]); + // 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: - killtag(pp[0]); + // DW1 only + KillTag(coroParam, pp[0]); return -1; - case LEFTOFFSET: - pp[0] = ltoffset(LEFTOFFSET); - return 0; + 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]); + 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]); + NewScene(coroParam, pp[0], pp[1], pp[2]); return -3; case NOBLOCKING: - noblocking(); + // Common to both DW1 & DW2 + NoBlocking(); + return 0; + + case NOPAUSE: + // DW2 only + bNoPause = true; return 0; case NOSCROLL: + // Common to both DW1 & DW2 pp -= 3; // 4 parameters - noscroll(pp[0], pp[1], pp[2], pp[3]); + NoScroll(pp[0], pp[1], pp[2], pp[3]); return -4; case OBJECTHELD: - objectheld(pp[0]); + // DW1 only + ObjectHeld(pp[0]); return -1; case OFFSET: - pp -= 1; // 2 parameters - offset(pp[0], pp[1]); - return -2; + // 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: - pp -= 5; // 6 parameters + // 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); + + if (coroParam) + return 0; + } + return -4; - if (pic->event == ENTER || pic->event == LEAVE) - play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myescEvent, false); - else - play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->actorid, false, 0, pic->escOn, pic->myescEvent, false); - return -6; + } 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]); + 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 - playsample(coroParam, pp[0], pp[1], pic->escOn, pic->myescEvent); + 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: - preparescene(pp[0]); + // DW1 only + PrepareScene(pp[0]); return -1; case PRINT: - pp -= 5; // 6 parameters - /* pp[2] was intended to be attribute */ - print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5], pic->escOn, pic->myescEvent); - return -6; + // 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: - printobj(coroParam, pp[0], pic->pinvo, pic->event); + // Common to both DW1 & DW2 + PrintObj(coroParam, pp[0], pic->pinvo, pic->event, pic->myEscape); return -1; case PRINTTAG: - printtag(pic->hpoly, pp[0]); + // Common to both DW1 & DW2 + PrintTag(pic->hPoly, pp[0], TinselV2 ? pic->idActor : 0, false); return -1; case QUITGAME: - quitgame(); + // Common to both DW1 & DW2 + QuitGame(); return 0; case RANDOM: + // Common to both DW1 & DW2 pp -= 2; // 3 parameters - pp[0] = dw_random(pp[0], pp[1], pp[2]); + pp[0] = RandomFn(pp[0], pp[1], pp[2]); return -2; // One holds return value case RESETIDLETIME: - resetidletime(); + // Common to both DW1 & DW2 + ResetIdleTime(); return 0; case RESTARTGAME: - restartgame(); + // Common to both DW1 & DW2 + RestartGame(); 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: - restore_scene(false); + // DW1 only + RestoreScene(coroParam, TRANS_CUT); return 0; - case RESTORE_SCENE: - restore_scene(true); + case RESUMELASTGAME: + // DW2 only + ResumeLastGame(); return 0; case RUNMODE: - pp[0] = runmode(); + // Common to both DW1 & DW2 + pp[0] = RunMode(); return 0; case SAMPLEPLAYING: - pp[0] = sampleplaying(pic->escOn, pic->myescEvent); + // DW1 only + pp[0] = SamplePlaying(pic->escOn, pic->myEscape); return 0; - case SAVE_SCENE: + case SAVESCENE: + // Common to both DW1 & DW2 if (*pResumeState == RES_1) *pResumeState = RES_2; else - save_scene(coroParam); + 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]); + ScalingReels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]); return -7; case SCANICON: - pp[0] = 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: - pp -= 3; // 4 parameters - scroll(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent); - return -4; + // 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: - setactor(pp[0]); + // DW1 only + SetActor(pp[0]); return -1; case SETBLOCK: - setblock(pp[0]); + // DW1 only + SetBlock(pp[0]); return -1; case SETEXIT: - setexit(pp[0]); + // DW1 only + SetExit(pp[0]); return -1; case SETINVLIMIT: + // Common to both DW1 & DW2 pp -= 1; // 2 parameters - setinvlimit(pp[0], pp[1]); + 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]); + SetInvSize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]); return -7; case SETLANGUAGE: - setlanguage((LANGUAGE)pp[0]); + // Common to both DW1 & DW2 + SetLanguage((LANGUAGE)pp[0]); return -1; case SETPALETTE: - setpalette(pp[0], pic->escOn, pic->myescEvent); - return -1; + // 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: - settag(pp[0]); + // 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]); + 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 == ENTER || pic->event == LEAVE) - splay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myescEvent); + 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->actorid, pic->escOn, pic->myescEvent); + 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(pp[0], pp[1], pp[2], pp[3]); + Stand(coroParam, pp[0], pp[1], pp[2], pp[3]); return -4; case STANDTAG: - standtag(pp[0], pic->hpoly); + // Common to both DW1 & DW2 + StandTag(pp[0], pic->hPoly); return -1; - case STOP: - stop(pp[0]); + 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: - stopmidi(); + // DW1 only + StopMidiFn(); return 0; case STOPSAMPLE: - stopsample(); - return 0; + // 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: - subtitles(pp[0]); + // 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], pic->escOn, pic->myescEvent); + 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]); + 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 (pic->event == ENTER || pic->event == LEAVE) - talk(coroParam, pp[0], pp[1], 0, pic->escOn, pic->myescEvent); + 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 - talk(coroParam, pp[0], pp[1], pic->actorid, pic->escOn, pic->myescEvent); + TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], pic->idActor, false, pic->escOn, pic->myEscape); return -2; case TALKAT: - pp -= 3; // 4 parameters - talkat(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent); - return -4; + // 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->myescEvent); + 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->myescEvent); + 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: - pp[0] = timer(pp[0]); + // Common to both DW1 & DW2 + pp[0] = TimerFn(pp[0]); return 0; - case TOPOFFSET: - pp[0] = ltoffset(TOPOFFSET); + case TOPIC: + // DW2 only + pp[0] = Topic(); return 0; case TOPPLAY: - pp -= 5; // 6 parameters - topplay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->actorid, false, 0, pic->escOn, pic->myescEvent); - return -6; + // 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: - topwindow(pp[0]); + // 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->myescEvent); + TryPlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape); return -2; + case UNDIMMUSIC: + // DW2 only + UnDimMusic(); + return 0; + case UNHOOKSCENE: - unhookscene(); + // Common to both DW1 & DW2 + UnHookSceneFn(); return 0; case UNTAGACTOR: - untagactor(pp[0]); + // DW1 only + UnTagActorFn(pp[0]); return -1; case VIBRATE: - vibrate(); - return 0; - - case WAITKEY: - waitkey(coroParam, pic->escOn, pic->myescEvent); + // 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->myescEvent); + 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->myescEvent); + WaitTime(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape); 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, pic->escOn, pic->myescEvent); + 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; - walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myescEvent, tmp); + 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; @@ -2941,24 +5522,65 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi return -3; case WALKINGACTOR: - pp -= 40; // 41 parameters - walkingactor(pp[0], (SCNHANDLE *)&pp[1]); - return -41; + // 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: - pp -= 2; // 3 parameters - walkpoly(coroParam, pp[0], pp[1], pic->hpoly, pic->escOn, pic->myescEvent); - return -3; + // 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: - pp -= 2; // 3 parameters - walktag(coroParam, pp[0], pp[1], pic->hpoly, pic->escOn, pic->myescEvent); - return -3; + // 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: - pp[0] = 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"); } @@ -2966,5 +5588,4 @@ int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pi error("Can't possibly get here"); } - } // end of namespace Tinsel |