scummvm/engines/tinsel/tinlib.cpp
Paul Gilbert b8ff3eb491 TINSEL: Ignore SV_SPEECHDELAY in DW2 Demo
This stops the long delays at the start of playing each sample

svn-id: r53717
2010-10-22 22:09:59 +00:00

5675 lines
128 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
* Glitter library functions.
*
* In the main called only from PCODE.C
* Function names are the same as Glitter code function names.
*
* To ensure exclusive use of resources and exclusive control responsibilities.
*/
#define BODGE
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/bmv.h"
#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/dialogs.h"
#include "tinsel/mareels.h"
#include "tinsel/move.h"
#include "tinsel/multiobj.h"
#include "tinsel/music.h"
#include "tinsel/object.h"
#include "tinsel/palette.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
#include "tinsel/sched.h"
#include "tinsel/scn.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h"
#include "tinsel/tinsel.h"
#include "tinsel/token.h"
namespace Tinsel {
//----------------- EXTERNAL GLOBAL DATA --------------------
// In DOS_DW.C
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.CPP
extern void ChangePalette(SCNHANDLE hPal);
// in PDISPLAY.CPP
extern void EnableTags();
extern void DisableTags();
bool DisableTagsIfEnabled();
extern void setshowstring();
// in SAVELOAD.CPP
extern int NewestSavedGame();
// in SCENE.CPP
extern void setshowpos();
extern int sceneCtr;
// in TINSEL.CPP
extern void SetCdChangeScene(SCNHANDLE hScene);
extern void SetHookScene(SCNHANDLE scene, int entrance, int transition);
extern void SetNewScene(SCNHANDLE scene, int entrance, int transition);
extern void UnHookScene();
extern void SuspendHook();
extern void UnSuspendHook();
#ifdef BODGE
// In HANDLE.CPP
bool ValidHandle(SCNHANDLE offset);
// In SCENE.CPP
SCNHANDLE GetSceneHandle();
#endif
//----------------- GLOBAL GLOBAL DATA --------------------
bool bEnableMenu;
static bool bInstantScroll = false;
static bool bEscapedCdPlay = false;
//----------------- LOCAL DEFINES --------------------
#define JAP_TEXT_TIME (2*ONE_SECOND)
/*----------------------------------------------------------------------*\
|* Library Procedure and Function codes *|
\*----------------------------------------------------------------------*/
enum MASTER_LIB_CODES {
ACTORATTR, ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY, ACTORREF,
ACTORRGB, ACTORSCALE, ACTORSON, ACTORXPOS, ACTORYPOS, ADDHIGHLIGHT,
ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC, AUXSCALE, BACKGROUND, BLOCKING,
CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT, CALLPROCESS, CALLSCENE, CALLTAG,
CAMERA, CDCHANGESCENE, CDDOCHANGE, CDENDACTOR, CDLOAD, CDPLAY, CLEARHOOKSCENE,
CLOSEINVENTORY, CONTROL, CONVERSATION, CONVTOPIC, CURSOR, CURSORXPOS, CURSORYPOS,
CUTSCENE, DECCONVW, DECCSTRINGS, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
DECLARELANGUAGE, DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELICON,
DELINV, DELTOPIC, DIMMUSIC, DROP, DROPEVERYTHING, DROPOUT, EFFECTACTOR, ENABLEMENU,
ENDACTOR, ESCAPE, ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEMIDI,
FADEOUT, FRAMEGRAB, FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, GRABMOVIE, HAILSCENE,
HASRESTARTED, HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, ININVENTORY, INSTANTSCROLL, INVDEPICT,
INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLBLOCK, KILLEXIT, KILLGLOBALPROCESS,
KILLPROCESS, KILLTAG, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
NOBLOCKING, NOPAUSE, NOSCROLL, OBJECTHELD, OFFSET, OTHEROBJECT, PAUSE, PLAY, PLAYMIDI,
PLAYMOVIE, PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR, POSTGLOBALPROCESS,
POSTOBJECT, POSTPROCESS, POSTTAG, PREPARESCENE, PRINT, PRINTCURSOR, PRINTOBJ, PRINTTAG,
QUITGAME, RANDOM, RESETIDLETIME, RESTARTGAME, RESTORESCENE, RESTORE_CUT,
RESUMELASTGAME, RUNMODE, SAMPLEPLAYING, SAVESCENE, SAY, SAYAT, SCALINGREELS,
SCANICON, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS, SENDACTOR, SENDGLOBALPROCESS,
SENDOBJECT, SENDPROCESS, SENDTAG, SETACTOR, SETBLOCK, SETBRIGHTNESS, SETEXIT, SETINVLIMIT,
SETINVSIZE, SETLANGUAGE, SETPALETTE, SETSYSTEMREEL, SETSYSTEMSTRING, SETSYSTEMVAR,
SETTAG, SETTIMER, SHELL, SHOWACTOR, SHOWBLOCK, SHOWEFFECT, SHOWMENU, SHOWPATH,
SHOWPOS, SHOWREFER, SHOWSTRING, SHOWTAG, SPLAY, STAND, STANDTAG, STARTGLOBALPROCESS,
STARTPROCESS, STARTTIMER, STOPMIDI, STOPSAMPLE, STOPWALK, SUBTITLES, SWALK, SWALKZ,
SYSTEMVAR, TAGACTOR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS, TALK, TALKAT,
TALKATS, TALKATTR, TALKPALETTEINDEX, TALKRGB, TALKVIA, TEMPTAGFONT, TEMPTALKFONT,
THISOBJECT, THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
TRYPLAYSAMPLE, UNDIMMUSIC, UNHOOKSCENE, UNTAGACTOR, VIBRATE, WAITFRAME, WAITKEY,
WAITSCROLL, WAITTIME, WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY, ZZZZZZ,
HIGHEST_LIBCODE
};
const MASTER_LIB_CODES DW1DEMO_CODES[] = {
ACTORREF, ACTORXPOS, ACTORYPOS, ADDTOPIC, ADDINV1, ADDINV2, AUXSCALE, BACKGROUND,
CAMERA, CONTROL, CONVERSATION, CONVTOPIC, HIGHEST_LIBCODE, CURSORXPOS, CURSORYPOS,
DECCONVW, DECCURSOR, DECTAGFONT, DECINVW, DECINV1, DECINV2, DECLEAD, DELICON,
DELINV, EVENT, HIGHEST_LIBCODE, HELDOBJECT, HIDEACTOR, ININVENTORY, HIGHEST_LIBCODE,
INVENTORY, HIGHEST_LIBCODE, KILLACTOR, KILLBLOCK, KILLTAG, SCREENXPOS,
HIGHEST_LIBCODE, MOVECURSOR, NEWSCENE, NOSCROLL, OBJECTHELD, OFFSET, HIGHEST_LIBCODE,
PLAY, PLAYSAMPLE, PREPARESCENE, PRINT, PRINTOBJ, PRINTTAG, RESTORESCENE, SAVESCENE,
SCANICON, SCROLL, SETACTOR, SETBLOCK, HIGHEST_LIBCODE, SETTAG, SETTIMER, SHOWPOS,
SPLAY, STAND, STANDTAG, STOPWALK, HIGHEST_LIBCODE, SWALK, TAGACTOR, TALK,
SCREENYPOS, UNTAGACTOR, VIBRATE, WAITKEY, WAITTIME, WALK, WALKINGACTOR, WALKPOLY,
WALKTAG, RANDOM, TIMER
};
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 DW2DEMO_CODES[] = {
ACTORBRIGHTNESS, ACTORDIRECTION, ACTORPALETTE, ACTORPRIORITY,
ACTORREF, ACTORRGB, ACTORSCALE, ACTORXPOS, ACTORYPOS,
ADDHIGHLIGHT, ADDINV, ADDINV1, ADDINV2, ADDOPENINV, ADDTOPIC,
BACKGROUND, CALLACTOR, CALLGLOBALPROCESS, CALLOBJECT,
CALLPROCESS, CALLSCENE, CALLTAG, CAMERA, CDCHANGESCENE,
CDDOCHANGE, CDLOAD, CDPLAY, CLEARHOOKSCENE, CLOSEINVENTORY,
CONTROL, CONVERSATION, CURSOR, CURSORXPOS, CURSORYPOS,
DECCONVW, DECCURSOR, DECFLAGS, DECINV1, DECINV2, DECINVW,
DECLEAD, DECSCALE, DECTAGFONT, DECTALKFONT, DELTOPIC,
DIMMUSIC, DROP, DROPOUT, EFFECTACTOR, ENABLEMENU, ENDACTOR,
ESCAPEOFF, ESCAPEON, EVENT, FACETAG, FADEIN, FADEOUT, FRAMEGRAB,
FREEZECURSOR, GETINVLIMIT, GHOST, GLOBALVAR, HASRESTARTED,
HAVE, HELDOBJECT, HIDEACTOR, HIDEBLOCK, HIDEEFFECT, HIDEPATH,
HIDEREFER, HIDETAG, HOLD, HOOKSCENE, IDLETIME, INSTANTSCROLL,
INVENTORY, INVPLAY, INWHICHINV, KILLACTOR, KILLGLOBALPROCESS,
KILLPROCESS, LOCALVAR, MOVECURSOR, MOVETAG, MOVETAGTO, NEWSCENE,
NOBLOCKING, NOPAUSE, NOSCROLL, OFFSET, OTHEROBJECT, PAUSE, PLAY,
PLAYMUSIC, PLAYRTF, PLAYSAMPLE, POINTACTOR, POINTTAG, POSTACTOR,
POSTGLOBALPROCESS, POSTOBJECT, POSTPROCESS, POSTTAG, PRINT,
PRINTCURSOR, PRINTOBJ, PRINTTAG, QUITGAME, RANDOM, RESETIDLETIME,
RESTARTGAME, RESTORESCENE, RUNMODE, SAVESCENE, SAY, SAYAT,
SCALINGREELS, SCREENXPOS, SCREENYPOS, SCROLL, SCROLLPARAMETERS,
SENDACTOR, SENDGLOBALPROCESS, SENDOBJECT, SENDPROCESS, SENDTAG,
SETBRIGHTNESS, SETINVLIMIT, SETINVSIZE, SETLANGUAGE, SETPALETTE,
SETSYSTEMSTRING, SETSYSTEMVAR, SHELL, SHOWACTOR, SHOWBLOCK,
SHOWEFFECT, SHOWPATH, SHOWREFER, SHOWTAG, STAND, STANDTAG,
STARTGLOBALPROCESS, STARTPROCESS, STARTTIMER, STOPWALK, SUBTITLES,
SWALK, SYSTEMVAR, TAGTAGXPOS, TAGTAGYPOS, TAGWALKXPOS, TAGWALKYPOS,
TALK, TALKAT, TALKPALETTEINDEX, TALKRGB, TALKVIA, THISOBJECT,
THISTAG, TIMER, TOPIC, TOPPLAY, TOPWINDOW, TRANSLUCENTINDEX,
UNDIMMUSIC, UNHOOKSCENE, WAITFRAME, WAITKEY, WAITSCROLL, WAITTIME,
WALK, WALKED, WALKEDPOLY, WALKEDTAG, WALKINGACTOR, WALKPOLY,
WALKTAG, WALKXPOS, WALKYPOS, WHICHCD, WHICHINVENTORY,
HIGHEST_LIBCODE
};
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
// precedes control(on).
static int controlX = 0, controlY = 0;
static int offtype = 0; // used by Control()
static uint32 lastValue = 0; // used by RandomFn()
static int scrollNumber = 0; // used by scroll()
static bool bNotPointedRunning = false; // Used in Printobj and PrintObjPointed
static COLORREF s_talkfontColor = 0;
//----------------- FORWARD REFERENCES --------------------
static int HeldObject();
static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape);
void ResetIdleTime();
static void SendTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape, bool *result);
static void StandTag(int actor, HPOLYGON hp);
void StopMidiFn();
void StopSample(int sample = -1);
static void StopWalk(int actor);
static void WaitScroll(CORO_PARAM, int myescEvent);
void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, int hold, bool igPath,
int zOverride, bool escOn, int myescTime);
//----------------- SUPPORT FUNCTIONS --------------------
/**
* For Scroll() and Offset(), work out top left for a
* given screen position.
*/
static void DecodeExtreme(EXTREME extreme, int *px, int *py) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
switch (extreme) {
case EX_BOTTOM:
*px = Loffset;
*py = BgHeight() - SCREEN_HEIGHT;
break;
case EX_BOTTOMLEFT:
*px = 0;
*py = BgHeight() - SCREEN_HEIGHT;
break;
case EX_BOTTOMRIGHT:
*px = BgWidth() - SCREEN_WIDTH;
*py = BgHeight() - SCREEN_HEIGHT;
break;
case EX_LEFT:
*px = 0;
*py = Toffset;
break;
case EX_RIGHT:
*px = BgWidth() - SCREEN_WIDTH;
*py = Toffset;
break;
case EX_TOP:
*px = Loffset;
*py = 0;
break;
case EX_TOPLEFT:
*px = *py = 0;
break;
case EX_TOPRIGHT:
*px = BgWidth() - SCREEN_WIDTH;
*py = 0;
break;
default:
break;
}
}
static void KillSelf(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
CORO_KILL_SELF();
CORO_END_CODE;
}
struct SCROLL_MONITOR {
int x;
int y;
int thisScroll;
int myEscape;
};
typedef SCROLL_MONITOR *PSCROLL_MONITOR;
/**
* Monitor a scrolling, allowing Escape to interrupt it
*/
static void ScrollMonitorProcess(CORO_PARAM, const void *param) {
int Loffset, Toffset;
const SCROLL_MONITOR *psm = (const SCROLL_MONITOR *)param;
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
do {
CORO_SLEEP(1);
// give up if have been superseded
if (psm->thisScroll != 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 colour into the DAC queue.
*/
void SetTextPal(COLORREF col) {
s_talkfontColor = col;
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;
else if (!_vm->_config->_textSpeed)
return strlen(pTstring) + ONE_SECOND;
else
return strlen(pTstring) + ONE_SECOND + (_vm->_config->_textSpeed * 5 * ONE_SECOND) / 100;
}
/**
* KeepOnScreen
*/
void KeepOnScreen(POBJECT pText, int *pTextX, int *pTextY) {
int shift;
// Not off the left
shift = MultiLeftmost(pText);
if (shift < 0) {
MultiMoveRelXY(pText, - shift, 0);
*pTextX -= shift;
}
// Not off the right
shift = MultiRightmost(pText);
if (shift > SCREEN_WIDTH) {
MultiMoveRelXY(pText, SCREEN_WIDTH - shift, 0);
*pTextX += SCREEN_WIDTH - shift;
}
// Not off the top
shift = MultiHighest(pText);
if (shift < 0) {
MultiMoveRelXY(pText, 0, - shift);
*pTextY -= shift;
}
// Not off the bottom
shift = MultiLowest(pText);
if (shift > SCREEN_BOX_HEIGHT2) {
MultiMoveRelXY(pText, 0, SCREEN_BOX_HEIGHT2 - shift);
*pTextX += SCREEN_WIDTH - shift;
}
}
/**
* Waits until the specified process is finished
*/
static void FinishWaiting(CORO_PARAM, const INT_CONTEXT *pic, bool *result = NULL) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (pic->resumeCode == RES_WAITING)
CORO_SLEEP(1);
if (result)
*result = pic->resumeCode == RES_FINISHED;
CORO_END_CODE;
}
void TinGetVersion(WHICH_VER which, char *buffer, int length) {
if (length > VER_LEN)
length = VER_LEN;
char *cptr = (char *)FindChunk(MASTER_SCNHANDLE, CHUNK_TIME_STAMPS);
switch (which) {
case VER_GLITTER:
memcpy(buffer, cptr, length);
break;
case VER_COMPILE:
memcpy(buffer, cptr + VER_LEN, length);
break;
}
}
/********************************************************************\
|***** Library functions *****|
\********************************************************************/
/**
* Set actor's attributes.
* - currently only the text colour.
*/
static void ActorAttr(int actor, int r1, int g1, int b1) {
storeActorAttr(actor, r1, g1, b1);
}
/**
* Behave as if actor has walked into a polygon with given brughtness.
*/
void ActorBrightness(int actor, int brightness) {
PMOVER pMover = GetMover(actor);
assert(pMover != NULL);
assert(brightness >= 0 && brightness <= 10);
MoverBrightness(pMover, brightness);
}
/**
* Return a moving actor's current direction.
*/
static int ActorDirection(int actor) {
PMOVER pMover = GetMover(actor);
assert(pMover);
return (int)GetMoverDirection(pMover);
}
/**
* Set actor's palette details for path brightnesses
*/
void ActorPalette(int actor, int startColour, int length) {
PMOVER pMover = GetMover(actor);
assert(pMover);
StoreMoverPalette(pMover, startColour, length);
}
/**
* Set actor's Z-factor.
*/
static void ActorPriority(int actor, int zFactor) {
SetActorZfactor(actor, zFactor);
}
/**
* Set actor's text colour.
*/
static void ActorRGB(int actor, COLORREF colour) {
SetActorRGB(actor, colour);
}
/**
* Return the actor's scale.
*/
static int ActorScale(int actor) {
PMOVER pMover = GetMover(actor);
assert(pMover);
return (int)GetMoverScale(pMover);
}
/**
* Returns the x or y position of an actor.
*/
static int ActorPos(int xory, int actor) {
int x, y;
GetActorPos(actor, &x, &y);
return (xory == ACTORXPOS) ? x : y;
}
/**
* Make all actors alive at the start of each scene.
*/
static void ActorsOn() {
setactorson();
}
/**
* Adds an icon to the conversation window.
*/
static void AddTopic(int icon) {
AddToInventory(INV_CONV, icon, false);
}
/**
* Place the object in inventory 1 or 2.
*/
static void AddInv(int invno, int object) {
// illegal inventory number
assert(invno == INV_1 || invno == INV_2 || invno == INV_OPEN || invno == INV_DEFAULT);
AddToInventory(invno, object, false);
}
/**
* Define an actor's walk and stand reels for an auxilliary scale.
*/
static void AuxScale(int actor, int scale, SCNHANDLE *rp) {
PMOVER pMover = GetMover(actor);
assert(pMover);
int j;
for (j = 0; j < 4; ++j)
pMover->walkReels[scale-1][j] = *rp++;
for (j = 0; j < 4; ++j)
pMover->standReels[scale-1][j] = *rp++;
for (j = 0; j < 4; ++j)
pMover->talkReels[scale-1][j] = *rp++;
}
/**
* Defines the background image for a scene.
*/
static void Background(CORO_PARAM, SCNHANDLE bfilm) {
StartupBackground(coroParam, bfilm);
}
/**
* Disable dynamic blocking for current scene.
*/
void Blocking(bool onOrOff) {
SetSysVar(ISV_NO_BLOCKING, !onOrOff);
}
/**
* Sets focus of the scroll process.
*/
static void Camera(int actor) {
ScrollFocus(actor);
}
/**
* Sets the CD Change Scene
*/
static void CdChangeScene(SCNHANDLE hScene) {
SetCdChangeScene(hScene);
}
/**
* CdDoChange
*/
void CdDoChange(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (!GotoCD())
return;
CORO_INVOKE_0(CdCD);
CdHasChanged();
CORO_END_CODE;
}
/**
* CdEndActor("actor")
*/
void CdEndActor(int actor, int myEscape) {
PMOVER pMover; // for if it's a moving actor
// Only do it if escaped!
if (myEscape && myEscape != GetEscEvents()) {
// End current graphic
dwEndActor(actor);
// un-hide movers
pMover = GetMover(actor);
if (pMover)
UnHideMover(pMover);
}
}
/**
* A CDPLAY() is imminent.
*/
static void CDload(SCNHANDLE start, SCNHANDLE next, int myEscape) {
assert(start && next && start != next); // cdload() fault
if (TinselV2) {
if (myEscape && myEscape != GetEscEvents()) {
bEscapedCdPlay = true;
return;
}
LoadExtraGraphData(start, next);
}
}
/**
* Clear the hooked scene (if any)
*/
static void ClearHookScene() {
SetHookScene(0, 0, TRANS_DEF);
}
/**
* Guess what.
*/
static void CloseInventory() {
KillInventory();
}
/**
* Turn off cursor and take control from player - and variations on the theme.
* OR Restore cursor and return control to the player.
*/
void Control(int param) {
if (TinselV2) {
if (param)
ControlOn();
else {
ControlOff();
switch (WhichInventoryOpen()) {
case INV_1:
case INV_2:
case INV_MENU:
KillInventory();
break;
default:
break;
}
}
return;
}
// Tinsel 1 handling code
bEnableMenu = false;
switch (param) {
case CONTROL_STARTOFF:
GetControlToken(); // Take control
DisableTags(); // Switch off tags
DwHideCursor(); // Blank out cursor
offtype = param;
break;
case CONTROL_OFF:
case CONTROL_OFFV:
case CONTROL_OFFV2:
if (TestToken(TOKEN_CONTROL)) {
GetControlToken(); // Take control
DisableTags(); // Switch off tags
GetCursorXYNoWait(&controlX, &controlY, true); // Store cursor position
// There may be a button timing out
GetToken(TOKEN_LEFT_BUT);
FreeToken(TOKEN_LEFT_BUT);
}
if (offtype == CONTROL_STARTOFF)
GetCursorXYNoWait(&controlX, &controlY, true); // Store cursor position
offtype = param;
if (param == CONTROL_OFF)
DwHideCursor(); // Blank out cursor
else if (param == CONTROL_OFFV) {
UnHideCursor();
FreezeCursor();
} else if (param == CONTROL_OFFV2) {
UnHideCursor();
}
break;
case CONTROL_ON:
if (offtype != CONTROL_OFFV2 && offtype != CONTROL_STARTOFF)
SetCursorXY(controlX, controlY);// ... where it was
FreeControlToken(); // Release control
if (!InventoryActive())
EnableTags(); // Tags back on
RestoreMainCursor(); // Re-instate cursor...
}
}
/**
* Open or close the conversation window.
*/
static void Conversation(CORO_PARAM, int fn, HPOLYGON hp, int actor, bool escOn, int myEscape) {
assert(hp != NOPOLY); // conversation() must (currently) be called from a polygon code block
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (fn == CONV_END) {
// Close down conversation
CloseDownConv();
} else if ((fn == CONV_TOP) || (fn == CONV_DEF) || (fn == CONV_BOTTOM)) {
// TOP of screen, Default (i.e. TOP of screen), or BOTTOM of screen
// If waiting is enabled, wait for ongoing scroll
if (TinselV2 && SysVar(SV_CONVERSATIONWAITS))
CORO_INVOKE_1(WaitScroll, myEscape);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
// Don't do it if already in a conversation
if (IsConvWindow())
return;
KillInventory();
if (TinselV2) {
// If this is from a tag polygon, get the associated
// actor (the one the polygon is named after), if any.
if (!actor) {
actor = GetTagPolyId(hp);
if (actor & ACTORTAG_KEY)
actor &= ~ACTORTAG_KEY;
else
actor = 0;
}
// Top or bottom; tag polygon or tagged actor
SetConvDetails((CONV_PARAM)fn, hp, actor);
} else {
convPos(fn);
ConvPoly(hp);
}
PopUpInventory(INV_CONV); // Conversation window
ConvAction(INV_OPENICON); // CONVERSATION event
}
CORO_END_CODE;
}
/**
* Add icon to conversation window's permanent default list.
*/
static void ConvTopic(int icon) {
PermaConvIcon(icon);
}
/**
* Cursor(on/off)
*/
void Cursor(int onoff) {
if (onoff) {
// Re-instate cursor
UnHideCursor();
} else {
// Blank out cursor
DwHideCursor();
}
}
/**
* Returns the x or y position of the cursor.
*/
static int CursorPos(int xory) {
int x, y;
GetCursorXY(&x, &y, true);
return (xory == CURSORXPOS) ? x : y;
}
/**
* Declare conversation window.
*/
static void DecConvW(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
idec_convw(text, MaxContents, MinWidth, MinHeight,
StartWidth, StartHeight, MaxWidth, MaxHeight);
}
/**
* Declare config strings.
*/
static void DecCStrings(SCNHANDLE *tp) {
setConfigStrings(tp);
}
/**
* Declare cursor's reels.
*/
static void DecCursor(SCNHANDLE hFilm) {
DwInitCursor(hFilm);
}
/**
* Declare the language flags.
*/
static void DecFlags(SCNHANDLE hFilm) {
setFlagFilms(hFilm);
}
/**
* Declare inventory 1's parameters.
*/
static void DecInv1(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
int MaxWidth, int MaxHeight) {
idec_inv1(text, MaxContents, MinWidth, MinHeight,
StartWidth, StartHeight, MaxWidth, MaxHeight);
}
/**
* Declare inventory 2's parameters.
*/
static void DecInv2(SCNHANDLE text, int MaxContents,
int MinWidth, int MinHeight,
int StartWidth, int StartHeight,
int MaxWidth, int MaxHeight) {
idec_inv2(text, MaxContents, MinWidth, MinHeight,
StartWidth, StartHeight, MaxWidth, MaxHeight);
}
/**
* Declare the bits that the inventory windows are constructed from.
*/
static void DecInvW(SCNHANDLE hf) {
setInvWinParts(hf);
}
/**
* DeclareLanguage
*/
static void DeclareLanguage(int languageId, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
LanguageFacts(languageId, hDescription, hFlagFilm);
}
/**
* Declare lead actor.
* @param id Actor Id
* @param rp Walk and stand reels for all the regular scales (v1 only)
* @param text Tag text (v1 only)
*/
static void DecLead(uint32 id, SCNHANDLE *rp = 0, SCNHANDLE text = 0) {
PMOVER pMover; // Moving actor structure
if (TinselV2) {
// Tinsel 2 only specifies the lead actor Id
SetLeadId(id);
RegisterMover(id);
} else {
Tag_Actor(id, text, TAG_DEF); // The lead actor is automatically tagged
SetLeadId(id); // Establish this as the lead
RegisterMover(id); // Establish as a moving actor
pMover = GetMover(id); // Get moving actor structure
assert(pMover);
// Store all those reels
int i, j;
for (i = 0; i < 5; ++i) {
for (j = 0; j < 4; ++j)
pMover->walkReels[i][j] = *rp++;
for (j = 0; j < 4; ++j)
pMover->standReels[i][j] = *rp++;
for (j = 0; j < 4; ++j)
pMover->talkReels[i][j] = *rp++;
}
for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
for (j = 0; j < 4; ++j) {
pMover->walkReels[i][j] = pMover->walkReels[4][j];
pMover->standReels[i][j] = pMover->standReels[2][j];
pMover->talkReels[i][j] = pMover->talkReels[4][j];
}
}
}
}
/**
* DecScale("actor", scale, 12*"reel")
* Define an actor's walk and stand reels for a scale.
*/
static void DecScale(int actor, int scale,
SCNHANDLE wkl, SCNHANDLE wkr, SCNHANDLE wkf, SCNHANDLE wka,
SCNHANDLE stl, SCNHANDLE str, SCNHANDLE stf, SCNHANDLE sta,
SCNHANDLE tal, SCNHANDLE tar, SCNHANDLE taf, SCNHANDLE taa) {
PMOVER pMover = GetMover(actor);
assert(pMover);
SetWalkReels(pMover, scale, wkl, wkr, wkf, wka);
SetStandReels(pMover, scale, stl, str, stf, sta);
SetTalkReels(pMover, scale, tal, tar, taf, taa);
}
/**
* Declare the text font.
*/
static void DecTagFont(SCNHANDLE hf) {
SetTagFontHandle(hf); // Store the font handle
if (TinselV0)
SetTalkFontHandle(hf); // Also re-use for talk text
}
/**
* Declare the text font.
*/
static void DecTalkFont(SCNHANDLE hf) {
SetTalkFontHandle(hf); // Store the font handle
}
/**
* Remove an icon from the conversation window.
*/
static void DelIcon(int icon) {
RemFromInventory(INV_CONV, icon);
}
/**
* Delete the object from inventory 1 or 2.
*/
static void DelInv(int object) {
if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
RemFromInventory(INV_2, object); // ...or 2 (whichever)
DropItem(object); // Stop holding it
}
/**
* DelTopic
*/
static void DelTopic(int icon) {
RemFromInventory(INV_CONV, icon);
}
/**
* DimMusic
*/
static void DimMusic() {
_vm->_pcmMusic->dim(true);
}
/**
* Delete the object from inventory 1 or 2.
*/
static void Drop(int object) {
if (object == -1)
object = HeldObject();
if (!RemFromInventory(INV_1, object)) // Remove from inventory 1...
RemFromInventory(INV_2, object); // ...or 2 (whichever)
DropItem(object); // Stop holding it
}
/**
* Delete all objects from inventory 1 and 2.
*/
static void DropEverything() {
HoldItem(NOOBJECT, false);
ClearInventory(INV_1);
ClearInventory(INV_2);
}
/**
* EnableMenu
*/
static void EnableMenu() {
bEnableMenu = true;
}
/**
* Kill an actor's current graphics.
*/
static void EndActor(int actor) {
dwEndActor(actor);
}
/**
* Get the actor to look at the polygon.
* If the actor is at the tag, do a StandTag().
*/
static void FaceTag(int actor, HPOLYGON hp) {
PMOVER pMover; // Moving actor structure
int nowx, nowy;
int nodex, nodey;
assert(hp != NOPOLY);
/*
* Get which moving actor it is
*/
pMover = GetMover(actor);
assert(pMover);
if (MoverHidden(pMover))
return;
/*
* Stop the actor
*/
StopWalk(actor);
/*
* Face the tag
*/
// See where node is and where actor is
GetPolyNode(hp, &nodex, &nodey);
GetActorPos(actor, &nowx, &nowy);
if (nowx == nodex && nowy == nodey) {
// Stood at the tag, don't face in silly direction
StandTag(actor, hp);
} else {
// Look towards polygon
GetPolyMidBottom(hp, &nodex, &nodey);
SetMoverDirection(pMover, GetDirection(nowx, nowy,
nodex, nodey,
GetMoverDirection(pMover),
NOPOLY, YB_X1_5));
SetMoverStanding(pMover);
}
}
/**
* FadeIn
*/
static void FadeIn() {
FadeInMedium(NULL);
}
/**
* FadeOut
*/
static void FadeOut() {
FadeOutMedium(NULL);
}
/**
* FadeMidi(in/out)
*/
static void FadeMidi(CORO_PARAM, int inout) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
assert(inout == FM_IN || inout == FM_OUT);
// To prevent compiler complaining
if (inout == FM_IN || inout == FM_OUT)
CORO_SLEEP(1);
CORO_END_CODE;
}
/**
* Freeze the cursor, or not.
*/
static void FreezeCursor(bool bFreeze) {
DoFreezeCursor(bFreeze);
}
/**
* Guess what.
*/
static int GetInvLimit(int invno) {
return InvGetLimit(invno);
}
/**
* Ghost
*/
static void Ghost(int actor, int 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.
*/
static bool HasRestarted() {
return bHasRestarted;
}
/**
* See if an object is in the inventory.
*/
int Have(int object) {
return (InventoryPos(object) != NOOBJECT);
}
/**
* Returns which object is currently held.
*/
static int HeldObject() {
return WhichItemHeld();
}
/**
* Hides the specified actor
*/
static void HideActorFn(CORO_PARAM, int ano) {
HideActor(coroParam, ano);
}
/**
* Turn a blocking polygon off.
*/
static void HideBlock(int block) {
DisableBlock(block);
}
/**
* Turn an effect polygon off.
*/
static void HideEffect(int effect) {
DisableEffect(effect);
}
/**
* Turn a path polygon off.
*/
static void HidePath(int path) {
DisablePath(path);
}
/**
* Turn a refer polygon off.
*/
static void HideRefer(int refer) {
DisableRefer(refer);
}
/**
* Turn a tag polygon off.
*/
static void HideTag(CORO_PARAM, int tag, HPOLYGON hp) {
// Tag could be zero, meaning calling tag
DisableTag(coroParam, tag ? tag : GetTagPolyId(hp));
}
/**
* Hold the specified object.
*/
static void Hold(int object) {
HoldItem(object, false);
}
/**
* HookScene(scene, entrance, transition)
*/
void HookScene(SCNHANDLE scene, int entrance, int transition) {
SetHookScene(scene, entrance, transition);
}
/**
* IdleTime
*/
static int IdleTime() {
// If control is off, system is not idle
if (!ControlIsOn()) {
// Player doesn't currently have control
ResetIdleTime();
return 0;
} else {
// Player has control - return time since last event
int x = getUserEventTime() / ONE_SECOND;
return x;
}
}
/**
* Set flag if InstantScroll(on), reset if InstantScroll(off)
*/
void InstantScroll(int onoff) {
bInstantScroll = (onoff != 0);
}
/**
* invdepict
*/
static void InvDepict(int object, SCNHANDLE hFilm) {
SetObjectFilm(object, hFilm);
}
/**
* See if an object is in the inventory.
*/
int InInventory(int object) {
return (InventoryPos(object) != INV_NOICON);
}
/**
* Open an inventory.
*/
static void Inventory(int invno, bool escOn, int myEscape) {
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
assert((invno == INV_1 || invno == INV_2)); // Trying to open illegal inventory
PopUpInventory(invno);
}
/**
* Alter inventory object's icon.
*/
static void InvPlay(int object, SCNHANDLE hFilm) {
SetObjectFilm(object, hFilm);
}
/**
* See if an object is in the inventory.
*/
static int InWhichInv(int object) {
if (WhichItemHeld() == object)
return 0;
if (IsInInventory(object, INV_1))
return 1;
if (IsInInventory(object, INV_2))
return 2;
return -1;
}
/**
* Kill an actor.
*/
static void KillActor(int actor) {
DisableActor(actor);
}
/**
* Turn a blocking polygon off.
*/
static void KillBlock(int block) {
DisableBlock(block);
}
/**
* Turn an exit off.
*/
static void KillExit(int exit) {
DisableExit(exit);
}
/**
* Turn a tag off.
*/
static void KillTag(CORO_PARAM, int tagno) {
DisableTag(coroParam, tagno);
}
/**
* Kills the specified global process
*/
static void KillGlobalProcess(uint32 procID) {
xKillGlobalProcess(procID);
}
/**
* Kills the specified process
*/
static void KillProcess(uint32 procID) {
KillSceneProcess(procID);
}
/**
* Returns the left or top offset of the screen.
*/
static int LToffset(int lort) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
return (lort == SCREENXPOS) ? Loffset : Toffset;
}
/**
* Set new cursor position.
*/
static void MoveCursor(int x, int y) {
SetCursorXY(x, y);
controlX = x; // Save these values so that
controlY = y; // control(on) doesn't undo this
}
/**
* MoveTag(tag, x, y)
*/
void MoveTag(int tag, int x, int y, HPOLYGON hp) {
// Tag could be zero, meaning calling tag
MovePolygon(TAG, tag ? tag : GetTagPolyId(hp), x, y);
}
/**
* MoveTagTo(tag, x, y)
*/
void MoveTagTo(int tag, int x, int y, HPOLYGON hp) {
// Tag could be zero, meaning calling tag
MovePolygonTo(TAG, tag ? tag : GetTagPolyId(hp), x, y);
}
/**
* Triggers change to a new scene.
*/
void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselV2) {
if (_vm->_bmv->MoviePlaying()) {
_vm->_bmv->AbortMovie();
CORO_SLEEP(2);
}
}
SetNewScene(scene, entrance, transition);
// Prevent tags and cursor re-appearing
if (TinselV2)
ControlStartOff();
else
GetControl(CONTROL_STARTOFF);
if (TinselV1)
++sceneCtr;
// Prevent code subsequent to this call running before scene changes
if (g_scheduler->getCurrentPID() != PID_MASTER_SCR)
CORO_KILL_SELF();
CORO_END_CODE;
}
/**
* Disable dynamic blocking for current scene.
*/
static void NoBlocking() {
SetNoBlocking(true);
}
/**
* Define a no-scroll boundary for the current scene.
*/
static void NoScroll(int x1, int y1, int x2, int y2) {
SetNoScroll(x1, y1, x2, y2);
}
/**
* Hold the specified object.
*/
static void ObjectHeld(int object) {
HoldItem(object);
}
/**
* Set the top left offset of the screen.
*/
void Offset(EXTREME extreme, int x, int y) {
KillScroll();
if (TinselV2)
DecodeExtreme(extreme, &x, &y);
PlayfieldSetPos(FIELD_WORLD, x, y);
}
/**
* OtherObject()
*/
int OtherObject(INV_OBJECT *pinvo) {
assert(pinvo != NULL);
// return held object or object clicked on - whichever is not the calling object
// pinvo->id is the calling object
// WhichItemHeld() gives the held object
// GetIcon() gives the object clicked on
assert(GetIcon() == pinvo->id || WhichItemHeld() == pinvo->id);
if (GetIcon() == pinvo->id)
return WhichItemHeld();
else
return GetIcon();
}
/**
* Play a film.
*/
static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int compit, int actorid, bool splay, int sfact,
bool escOn, int myEscape, bool bTop) {
assert(hFilm != 0); // play(): Trying to play NULL film
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Don't do CDPlay() for now if already escaped
if (bEscapedCdPlay) {
bEscapedCdPlay = false;
return;
}
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
// If this actor is dead, call a stop to the calling process
if (actorid && !actorAlive(actorid))
CORO_KILL_SELF();
// 7/4/95
if (!escOn)
myEscape = GetEscEvents();
if (compit == 1) {
// Play to completion before returning
CORO_INVOKE_ARGS(PlayFilmc, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
} else if (compit == 2) {
error("play(): compit == 2 - please advise John");
} else {
// Kick off the play and return.
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actorid, splay, sfact, escOn, myEscape, bTop));
}
CORO_END_CODE;
}
/**
* Play a film
*/
static void Play(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myEscape,
bool bTop, TINSEL_EVENT event, HPOLYGON hPoly, int taggedActor) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
assert(hFilm != 0);
// Don't do CdPlay() for now if already escaped
if (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.
*/
static void PlayMidi(CORO_PARAM, SCNHANDLE hMidi, int loop, bool complete) {
// FIXME: This is a workaround for the FIXME below
if (GetMidiVolume() == 0 || TinselV1PSX)
return;
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
assert(loop == MIDI_DEF || loop == MIDI_LOOP);
PlayMidiSequence(hMidi, loop == MIDI_LOOP);
// FIXME: The following check messes up the script arguments when
// entering the secret door in the bookshelf in the library,
// leading to a crash, when the music volume is set to 0 (MidiPlaying()
// always false then).
//
// Why exactly this happens is unclear. An analysis of the involved
// script(s) might reveal more.
//
// Note: This check&sleep was added in DW v2. It was most likely added
// to ensure that the MIDI song started playing before the next opcode
// is executed.
if (!MidiPlaying())
CORO_SLEEP(1);
if (complete) {
while (MidiPlaying())
CORO_SLEEP(1);
}
CORO_END_CODE;
}
/**
* Plays a movie
*/
static void PlayMovie(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
CORO_BEGIN_CONTEXT;
int i;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (myEscape && myEscape != GetEscEvents())
return;
// Get rid of the cursor
for (_ctx->i = 0; _ctx->i < 3; _ctx->i++) {
DwHideCursor();
DropCursor();
CORO_SLEEP(1);
}
// They claim to be getting "Can't play two movies at once!" error
while (_vm->_bmv->MoviePlaying())
CORO_SLEEP(1);
// Play the movie
CORO_INVOKE_2(_vm->_bmv->PlayBMV, hFileStem, myEscape);
CORO_END_CODE;
}
/**
* Play some music
*/
static void PlayMusic(int tune) {
_vm->_pcmMusic->startPlay(tune);
}
/**
* Play a sample.
* Tinsel 1 version
*/
static void PlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
Audio::SoundHandle handle;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Don't play SFX if voice is already playing
if (_vm->_mixer->hasActiveChannelOfType(Audio::Mixer::kSpeechSoundType))
return;
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents()) {
_vm->_sound->stopAllSamples(); // Stop any currently playing sample
return;
}
if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
_vm->_sound->playSample(sample, Audio::Mixer::kSFXSoundType, &_ctx->handle);
if (bComplete) {
while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
// Abort if escapable and ESCAPE is pressed
if (escOn && myEscape != GetEscEvents()) {
_vm->_mixer->stopHandle(_ctx->handle);
break;
}
CORO_SLEEP(1);
}
}
} else {
// Prevent Glitter lock-up
CORO_SLEEP(1);
}
CORO_END_CODE;
}
/**
* Play a sample
* Tinsel 2 version
*/
static void PlaySample(CORO_PARAM, int sample, int x, int y, int flags, int myEscape) {
int priority;
CORO_BEGIN_CONTEXT;
Audio::SoundHandle handle;
int myEscape;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->myEscape = myEscape;
// Not escapable if PlaySample(..., s)
if (flags & PS_SUSTAIN) {
_ctx->myEscape = 0;
priority = PRIORITY_SPLAY2;
} else {
priority = PRIORITY_SPLAY1;
}
// Don't do anything if it's already been escaped
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
return;
if (_vm->_config->_soundVolume != 0 && _vm->_sound->sampleExists(sample)) {
if (x == 0)
x = -1;
_vm->_sound->playSample(sample, 0, false, x, y, priority, Audio::Mixer::kSFXSoundType,
&_ctx->handle);
if (flags & PS_COMPLETE) {
while (_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
// Abort if escapable and ESCAPE is pressed
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents()) {
_vm->_mixer->stopHandle(_ctx->handle);
break;
}
CORO_SLEEP(1);
}
}
} else {
// Prevent Glitter lock-up
CORO_SLEEP(1);
}
CORO_END_CODE;
}
/**
* Move the cursor to the tagged actor's tag point.
*/
void PointActor(int actor) {
int x, y;
// Only do this if the function is enabled
if (!SysVar(SV_ENABLEPOINTTAG))
return;
assert(IsTaggedActor(actor));
GetActorTagPos(actor, &x, &y, true);
_vm->setMousePosition(Common::Point(x, y));
}
/**
* Move the cursor to the tag's tag point.
*/
static void PointTag(int tagno, HPOLYGON hp) {
int x, y;
SCNHANDLE junk;
// Only do this if the function is enabled
if (!SysVar(SV_ENABLEPOINTTAG))
return;
// Tag could be zero, meaning calling tag
if (tagno == 0)
tagno = GetTagPolyId(hp);
GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
_vm->setMousePosition(Common::Point(x, y));
}
/**
* PostActor("actor", event)
*/
static void PostActor(CORO_PARAM, int actor, TINSEL_EVENT event, HPOLYGON hp,
int taggedActor, int myEscape) {
if (actor == -1) {
actor = taggedActor;
assert(hp == NOPOLY && taggedActor);
assert(IsTaggedActor(actor));
}
if (IsTaggedActor(actor)) {
assert(actor);
ActorEvent(coroParam, actor, event, false, myEscape);
} else {
PostTag(coroParam, actor | ACTORTAG_KEY, event, hp, myEscape);
}
}
/**
* PostGlobalProcess(process#, event)
*/
static void PostGlobalProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
GlobalProcessEvent(coroParam, procId, event, false, myEscape);
}
/**
* PostObject(object, event)
*/
static void PostObject(CORO_PARAM, int object, TINSEL_EVENT event, int myEscape) {
ObjectEvent(coroParam, object, event, false, myEscape);
}
/**
* PostProcess(process#, event)
*/
static void PostProcess(CORO_PARAM, int procId, TINSEL_EVENT event, int myEscape) {
SceneProcessEvent(coroParam, procId, event, false, myEscape);
}
/**
* Posts an event to a specified tag
*/
static void PostTag(CORO_PARAM, int tagno, TINSEL_EVENT event, HPOLYGON hp, int myEscape) {
// Tag could be zero, meaning calling tag
if (tagno == 0) {
assert(hp != NOPOLY);
PolygonEvent(coroParam, hp, event, 0, false, myEscape);
} else {
assert(IsTagPolygon(tagno));
PolygonEvent(coroParam, GetTagHandle(tagno), event, 0, false, myEscape);
}
}
/**
* Trigger pre-loading of a scene's data.
*/
static void PrepareScene(SCNHANDLE scene) {
#ifdef BODGE
if (!ValidHandle(scene))
return;
#endif
}
/**
* Print the given text at the given place for the given time.
*/
static void Print(CORO_PARAM, int x, int y, SCNHANDLE text, int time, bool bSustain, bool escOn, int myEscape) {
if (TinselV2)
escOn = myEscape != 0;
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
int myleftEvent;
bool bSample; // Set if a sample is playing
Audio::SoundHandle handle;
int timeout;
int time;
CORO_END_CONTEXT(_ctx);
bool bJapDoPrintText; // Bodge to get-around Japanese bodge
CORO_BEGIN_CODE(_ctx);
_ctx->pText = NULL;
_ctx->bSample = false;
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
if (!TinselV2) {
// Kick off the voice sample
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
}
}
// Get the string
LoadStringRes(text, TextBufferAddr(), TBUFSZ);
// Calculate display time
bJapDoPrintText = false;
if (time == 0) {
// This is a 'talky' print
_ctx->time = TextTime(TextBufferAddr());
// Cut short-able if sustain was not set
_ctx->myleftEvent = bSustain ? 0 : GetLeftEvents();
} else {
_ctx->time = time * ONE_SECOND;
_ctx->myleftEvent = (TinselV2 && !bSustain) ? GetLeftEvents() : 0;
if (isJapanMode())
bJapDoPrintText = true;
}
// Print the text
if (TinselV2) {
int Loffset, Toffset;
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
_ctx->pText = ObjectTextOut(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() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
int Loffset, Toffset; // Screen position
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
_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);
/*
* New feature: Don't go off the side of the background
*/
int shift;
shift = MultiRightmost(_ctx->pText) + 2;
if (shift >= BgWidth()) // Not off right
MultiMoveRelXY(_ctx->pText, BgWidth() - shift, 0);
shift = MultiLeftmost(_ctx->pText) - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(_ctx->pText, -shift, 0);
shift = MultiLowest(_ctx->pText);
if (shift > BgHeight()) // Not off bottom
MultiMoveRelXY(_ctx->pText, 0, BgHeight() - shift);
}
// Give up if nothing printed and no sample
if (_ctx->pText == NULL && !_ctx->bSample)
return;
// Leave it up until time runs out or whatever
if (TinselV2) {
do {
CORO_SLEEP(1);
// Cancelled?
if ( (myEscape && myEscape != GetEscEvents())
|| (!bSustain && LeftEventChange(_ctx->myleftEvent)))
break;
} while (_ctx->time-- >= 0);
} else {
_ctx->timeout = SAMPLETIMEOUT;
do {
CORO_SLEEP(1);
// Abort if escapable and ESCAPE is pressed
// Abort if left click - hardwired feature for talky-print!
// Will be ignored if myleftevent happens to be 0!
// Abort if sample times out
if ((escOn && myEscape != GetEscEvents())
|| (_ctx->myleftEvent && _ctx->myleftEvent != GetLeftEvents())
|| (_ctx->bSample && --_ctx->timeout <= 0))
break;
if (_ctx->bSample) {
// Wait for sample to end whether or not
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
// No text or speed modification - just depends on sample
break;
} else {
// Must wait for time
_ctx->bSample = false;
}
}
} else {
// No sample - just depends on time
if (_ctx->time-- <= 0)
break;
}
} while (1);
}
// Delete the text
if (_ctx->pText != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_vm->_mixer->stopHandle(_ctx->handle);
CORO_END_CODE;
}
static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item);
static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText);
/**
* Print the given inventory object's name or whatever.
*/
static void PrintObj(CORO_PARAM, const SCNHANDLE hText, const INV_OBJECT *pinvo, const int event, int myEscape) {
CORO_BEGIN_CONTEXT;
OBJECT *pText; // text object pointer
int textx, texty;
int item;
bool bSample;
int sub;
Audio::SoundHandle handle;
int ticks;
int timeout;
bool bTookControl;
int myEscape;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
assert(pinvo != 0); // PrintObj() may only be called from an object code block
_ctx->myEscape = myEscape;
if (hText == (SCNHANDLE)-1) { // 'OFF'
bNotPointedRunning = true;
return;
}
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) {
bNotPointedRunning = true; // Get POINTED text to die
CORO_SLEEP(1); // Give it chance to
} else if (!TinselV2)
bNotPointedRunning = false; // There may have been an OFF without an ON
// Make multi-ones escape
if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->myEscape)
_ctx->myEscape = GetEscEvents();
// Loop once for Tinsel 1 strings, and for Tinsel 2 however many lines are needed
for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
if (_ctx->myEscape && _ctx->myEscape != GetEscEvents())
break;
if (!_vm->_sound->sampleExists(hText))
_ctx->bSample = false;
else {
// Kick off the voice sample
_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK,
Audio::Mixer::kSpeechSoundType, &_ctx->handle);
_ctx->bSample = true;
}
// Display the text and set it's Z position
if (event == POINTED || (!isJapanMode() && (_vm->_config->_useSubtitles || !_ctx->bSample))) {
int xshift;
// Get the text string
if (TinselV2)
LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
else
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
_ctx->pText = ObjectTextOut(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 || _vm->_config->_textSpeed == DEFTEXTSPEED) {
// No text or speed modification - just depends on sample
break;
} else {
// Must wait for time
_ctx->bSample = false;
}
}
// Decrement the subtitles timeout counter
if (_ctx->ticks > 0) --_ctx->ticks;
} else {
// No sample - just depends on time
if (_ctx->ticks-- <= 0)
break;
}
}
if (_ctx->bTookControl)
ControlOn(); // Free control if we took it
}
} else {
if (event == POINTED) {
// FIXME: Is there ever an associated sound if in POINTED mode???
assert(!_vm->_sound->sampleExists(hText));
CORO_INVOKE_ARGS(PrintObjPointed, (CORO_SUBCTX, hText, pinvo, _ctx->pText, _ctx->textx, _ctx->texty, _ctx->item));
} else {
CORO_INVOKE_2(PrintObjNonPointed, hText, _ctx->pText);
}
}
// Delete the text, if haven't already
if (_ctx->pText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
// If it hasn't already finished, stop sample
if (_ctx->bSample)
_vm->_mixer->stopHandle(_ctx->handle);
}
// Let POINTED text back in if this is the last
if (event != POINTED)
bNotPointedRunning = false;
CORO_END_CODE;
}
static void PrintObjPointed(CORO_PARAM, const SCNHANDLE text, const INV_OBJECT *pinvo, OBJECT *&pText, const int textx, const int texty, const int item) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Have to give way to non-POINTED-generated text
// and go away if the item gets picked up
int x, y;
do {
// Give up if this item gets picked up
if (WhichItemHeld() == pinvo->id)
break;
// Give way to non-POINTED-generated text
if (bNotPointedRunning) {
// Delete the text, and wait for the all-clear
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), pText);
pText = NULL;
while (bNotPointedRunning)
CORO_SLEEP(1);
GetCursorXY(&x, &y, false);
if (InvItem(&x, &y, false) != item)
break;
// Re-display in the same place
LoadStringRes(text, TextBufferAddr(), TBUFSZ);
pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, textx, texty, GetTagFontHandle(), TXT_CENTRE);
assert(pText); // PrintObj() string produced NULL text
MultiSetZPosition(pText, Z_INV_ITEXT);
}
CORO_SLEEP(1);
// Carry on until the cursor leaves this icon
GetCursorXY(&x, &y, false);
} while (InvItemId(x, y) == pinvo->id);
CORO_END_CODE;
}
static void PrintObjNonPointed(CORO_PARAM, const SCNHANDLE text, const OBJECT *pText) {
CORO_BEGIN_CONTEXT;
bool bSample; // Set if a sample is playing
Audio::SoundHandle handle;
int myleftEvent;
bool took_control;
int ticks;
int timeout;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Kick off the voice sample
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(text)) {
_vm->_sound->playSample(text, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
_ctx->bSample = _vm->_mixer->isSoundHandleActive(_ctx->handle);
} else
_ctx->bSample = false;
_ctx->myleftEvent = GetLeftEvents();
_ctx->took_control = GetControl(CONTROL_OFF);
// Display for a time, but abort if conversation gets hidden
if (isJapanMode())
_ctx->ticks = JAP_TEXT_TIME;
else if (pText)
_ctx->ticks = TextTime(TextBufferAddr());
else
_ctx->ticks = 0;
_ctx->timeout = SAMPLETIMEOUT;
do {
CORO_SLEEP(1);
--_ctx->timeout;
// Abort if left click - hardwired feature for talky-print!
// Abort if sample times out
// Abort if conversation hidden
if (_ctx->myleftEvent != GetLeftEvents() || _ctx->timeout <= 0 || ConvIsHidden())
break;
if (_ctx->bSample) {
// Wait for sample to end whether or not
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
if (pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
// No text or speed modification - just depends on sample
break;
} else {
// Must wait for time
_ctx->bSample = false;
}
}
// Decrement the subtitles timeout counter
if (_ctx->ticks > 0) --_ctx->ticks;
} else {
// No sample - just depends on time
if (_ctx->ticks-- <= 0)
break;
}
} while (1);
bNotPointedRunning = false; // Let POINTED text back in
if (_ctx->took_control)
Control(CONTROL_ON); // Free control if we took it
_vm->_mixer->stopHandle(_ctx->handle);
CORO_END_CODE;
}
/**
* Register the fact that this poly would like its tag displayed.
*/
static void PrintTag(HPOLYGON hp, SCNHANDLE text, int actor = 0, bool bCursor = false) {
// printtag() may only be called from a polygon code block in Tinsel 1, or
// additionally from a moving actor code block in Tinsel 2
assert((hp != NOPOLY) || (TinselV2 && (actor != 0)));
if (hp != NOPOLY) {
// Poly handling
if (TinselV2)
SetPolyTagWanted(hp, true, bCursor, text);
else if (PolyTagState(hp) == TAG_OFF) {
SetPolyTagState(hp, TAG_ON);
SetPolyTagHandle(hp, text);
}
} else {
// Moving actor handling
SetActorTagWanted(actor, true, bCursor, text);
}
}
/**
* Quits the game
*/
static void QuitGame() {
StopMidi();
StopSample();
_vm->quitGame();
}
/**
* Return a random number between optional limits.
*/
static int RandomFn(int n1, int n2, int norpt) {
int i = 0;
uint32 value;
// In DW1 demo, upper/lower limit can be reversed
if (n2 < n1) SWAP(n1, n2);
do {
value = n1 + _vm->getRandomNumber(n2 - n1);
} while ((lastValue == value) && (norpt == RAND_NORPT) && (++i <= 10));
lastValue = value;
return value;
}
/**
* ResetIdleTime
*/
void ResetIdleTime() {
resetUserEventTime();
}
/**
* FnRestartGame
*/
void FnRestartGame() {
// TODO: Tinsel 2 comments out the 2 calls, but I'm not sure that this should be done
StopMidi();
StopSample();
bRestart = true;
sceneCtr = 0;
}
/**
* Restore saved scene.
*/
static void RestoreScene(CORO_PARAM, TRANSITS transition) {
// COROUTINE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselV2) {
if (_vm->_bmv->MoviePlaying()) {
_vm->_bmv->AbortMovie();
CORO_SLEEP(2);
}
CuttingScene(false);
} else {
UnSuspendHook();
}
TinselRestoreScene(transition == TRANS_FADE);
CORO_END_CODE;
}
/**
* Resumes the last game
*/
void ResumeLastGame() {
RestoreGame(NewestSavedGame());
}
/**
* Returns the current run mode
*/
static int RunMode() {
return clRunMode;
}
/**
* SamplePlaying
*/
static bool SamplePlaying(bool escOn, int myEscape) {
// escape effects introduced 14/12/95 to fix
// while (sampleplaying()) pause;
if (escOn && myEscape != GetEscEvents())
return false;
return _vm->_sound->sampleIsPlaying();
}
/**
* Save current scene.
*/
void SaveScene(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselV2) {
CuttingScene(true);
SendSceneTinselProcess(LEAVE_T2);
CORO_GIVE_WAY;
CORO_INVOKE_0(TinselSaveScene);
} else {
CORO_INVOKE_0(TinselSaveScene);
SuspendHook();
}
CORO_END_CODE;
}
/**
* ScalingReels
*/
static void ScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
SetScalingReels(actor, scale, direction, left, right, forward, away);
}
/**
* Return the icon that caused the CONVERSE event.
*/
static int ScanIcon() {
return GetIcon();
}
/**
* Scroll the screen to target co-ordinates.
*/
static void Scroll(CORO_PARAM, EXTREME extreme, int xp, int yp, int xIter, int yIter, bool bComp, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int thisScroll;
int x, y;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
++scrollNumber;
_ctx->x = xp;
_ctx->y = yp;
if ((TinselV2 && bInstantScroll) || (escOn && myEscape != GetEscEvents())) {
// Instant completion!
Offset(extreme, _ctx->x, _ctx->y);
} else {
_ctx->thisScroll = scrollNumber;
if (TinselV2)
DecodeExtreme(extreme, &_ctx->x, &_ctx->y);
ScrollTo(_ctx->x, _ctx->y, xIter, yIter);
if (bComp) {
int Loffset, Toffset;
do {
CORO_SLEEP(1);
// If escapable and ESCAPE is pressed...
if (escOn && myEscape != GetEscEvents()) {
// Instant completion!
Offset(extreme, _ctx->x, _ctx->y);
break;
}
// give up if have been superseded
if (_ctx->thisScroll != scrollNumber)
CORO_KILL_SELF();
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
} 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.
*/
static void SetActor(int actor) {
EnableActor(actor);
}
/**
* Turn a blocking polygon on.
*/
static void SetBlock(int blockno) {
EnableBlock(blockno);
}
/**
* Turn an exit on.
*/
static void SetExit(int exitno) {
EnableExit(exitno);
}
/**
* Guess what.
*/
static void SetInvLimit(int invno, int n) {
InvSetLimit(invno, n);
}
/**
* Guess what.
*/
static void SetInvSize(int invno, int MinWidth, int MinHeight,
int StartWidth, int StartHeight, int MaxWidth, int MaxHeight) {
InvSetSize(invno, MinWidth, MinHeight, StartWidth, StartHeight, MaxWidth, MaxHeight);
}
/**
* Guess what.
*/
static void SetLanguage(LANGUAGE lang) {
assert(lang == TXT_ENGLISH || lang == TXT_FRENCH
|| lang == TXT_GERMAN || lang == TXT_ITALIAN
|| lang == TXT_SPANISH); // ensure language is valid
ChangeLanguage(lang);
}
/**
* Set palette
*/
static void SetPalette(SCNHANDLE hPal, bool escOn, int myEscape) {
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
ChangePalette(hPal);
}
/**
* SetSystemString
*/
static void SetSystemString(int stringId, SCNHANDLE hString) {
SetSysString(stringId, hString);
}
/**
* Set a system variable
*/
static void SetSystemVar(int varId, int newValue) {
SetSysVar(varId, newValue);
}
/**
* Turn a tag on.
*/
static void SetTag(CORO_PARAM, int tagno) {
EnableTag(coroParam, tagno);
}
/**
* Initialise a timer.
*/
static void SetTimer(int timerno, int start, bool up, bool frame) {
StartTimer(timerno, start, up != 0, frame != 0);
}
/**
* Shell("cmdline")
*/
static void Shell(SCNHANDLE commandLine) {
LoadStringRes(commandLine, TextBufferAddr(), TBUFSZ);
error("Tried to execute shell command \"%s\"", TextBufferAddr());
}
/**
* Don't hide an actors graphics.
*/
static void ShowActorFn(CORO_PARAM, int actor) {
ShowActor(coroParam, actor);
}
/**
* Turn a blocking polygon on.
*/
void ShowBlock(int blockno) {
EnableBlock(blockno);
}
/**
* Turn an effect polygon on.
*/
void ShowEffect(int effect) {
EnableEffect(effect);
}
#ifdef DEBUG
/**
* Enable display of diagnostic co-ordinates.
*/
static void showpos() {
setshowpos();
}
/**
* Enable display of diagnostic co-ordinates.
*/
static void showstring() {
setshowstring();
}
#endif
/**
* Shows the main menu
*/
static void ShowMenu() {
OpenMenu(MAIN_MENU);
}
/**
* Turn a path on.
*/
static void ShowPath(int path) {
EnablePath(path);
}
/**
* Turn a refer on.
*/
void ShowRefer(int refer) {
EnableRefer(refer);
}
/**
* Turn a tag on.
*/
static void ShowTag(CORO_PARAM, int tag, HPOLYGON hp) {
// Tag could be zero, meaning calling tag
EnableTag(coroParam, tag ? tag : GetTagPolyId(hp));
}
/**
* Special play - slow down associated actor's movement while the play
* is running. After the play, position the actor where the play left
* it and continue walking, if the actor still is.
*/
static void SPlay(CORO_PARAM, int sf, SCNHANDLE film, int x, int y, bool complete, int actorid, bool escOn, int myEscape) {
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
Play(coroParam, film, x, y, complete, actorid, true, sf, escOn, myEscape, false);
}
/**
* (Re)Position an actor.
* If moving actor is not around yet in this scene, start it up.
*/
void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm) {
CORO_BEGIN_CONTEXT;
PMOVER pMover; // Moving actor structure
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->pMover = GetMover(actor);
assert(!TinselV2 || (_ctx->pMover != NULL));
if (_ctx->pMover) {
if (TinselV2) {
// New special. If no paths, just ignore this
if (PathCount() == 0)
return;
// Another new special.
// If lead actor, and TalkVia, ignore
if ((actor == GetLeadId() || actor == LEAD_ACTOR) && SysVar(ISV_DIVERT_ACTOR))
return;
}
if (!MoverIs(_ctx->pMover)) {
// create a moving actor process
MoverProcessCreate(x, y, (actor == LEAD_ACTOR) ? GetLeadId() : actor, _ctx->pMover);
if (hFilm == TF_NONE) {
// Make sure there is an assigned actorObj
while (!_ctx->pMover->actorObj)
CORO_SLEEP(1);
SetMoverStanding(_ctx->pMover);
} else {
// Check hFilm against certain constants. Note that a switch statement isn't
// used here because it would interfere with our co-routine implementation
if (hFilm == TF_UP) {
if (TinselV2) CORO_GIVE_WAY;
SetMoverDirection(_ctx->pMover, AWAY);
SetMoverStanding(_ctx->pMover);
} else if (hFilm == TF_DOWN) {
if (TinselV2) CORO_GIVE_WAY;
SetMoverDirection(_ctx->pMover, FORWARD);
SetMoverStanding(_ctx->pMover);
} else if (hFilm == TF_LEFT) {
if (TinselV2) CORO_GIVE_WAY;
SetMoverDirection(_ctx->pMover, LEFTREEL);
SetMoverStanding(_ctx->pMover);
} else if (hFilm == TF_RIGHT) {
if (TinselV2) CORO_GIVE_WAY;
SetMoverDirection(_ctx->pMover, RIGHTREEL);
SetMoverStanding(_ctx->pMover);
} else if (hFilm != TF_NONE) {
if (TinselV2) CORO_GIVE_WAY;
AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
}
}
} else {
switch (hFilm) {
case TF_NONE:
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
break;
case TF_UP:
SetMoverDirection(_ctx->pMover, AWAY);
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
SetMoverStanding(_ctx->pMover);
break;
case TF_DOWN:
SetMoverDirection(_ctx->pMover, FORWARD);
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
SetMoverStanding(_ctx->pMover);
break;
case TF_LEFT:
SetMoverDirection(_ctx->pMover, LEFTREEL);
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
SetMoverStanding(_ctx->pMover);
break;
case TF_RIGHT:
SetMoverDirection(_ctx->pMover, RIGHTREEL);
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
SetMoverStanding(_ctx->pMover);
break;
default:
if (x != -1 && y != -1)
PositionMover(_ctx->pMover, x, y);
AlterMover(_ctx->pMover, hFilm, AR_NORMAL);
break;
}
}
} else if (actor == NULL_ACTOR) {
//
} else {
assert(hFilm != 0); // Trying to play NULL film
// Kick off the play and return.
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, x, y, actor, false, 0, false, 0, false));
}
CORO_END_CODE;
}
/**
* Position the actor at the polygon's tag node.
*/
static void StandTag(int actor, HPOLYGON hp) {
SCNHANDLE hFilm;
int pnodex, pnodey;
assert(hp != NOPOLY); // StandTag() may only be called from a polygon code block
// Where to stand
GetPolyNode(hp, &pnodex, &pnodey);
// Lead actor uses tag node film
hFilm = GetPolyFilm(hp);
// other actors can use direction
if (TinselV2) {
if (actor != LEAD_ACTOR && actor != GetLeadId()
&& hFilm != TF_UP && hFilm != TF_DOWN
&& hFilm != TF_LEFT && hFilm != TF_RIGHT)
hFilm = 0;
Stand(nullContext, actor, pnodex, pnodey, hFilm);
} else if (hFilm && (actor == LEAD_ACTOR || actor == GetLeadId()))
Stand(nullContext, actor, pnodex, pnodey, hFilm);
else
Stand(nullContext, actor, pnodex, pnodey, 0);
}
/**
* StartGlobalProcess
*/
static void StartGlobalProcess(CORO_PARAM, uint32 procID) {
GlobalProcessEvent(coroParam, procID, STARTUP, false, 0);
}
/**
* StartProcess
*/
static void StartProcess(CORO_PARAM, uint32 procID) {
SceneProcessEvent(coroParam, procID, STARTUP, false, 0);
}
/**
* Initialise a timer.
*/
static void StartTimerFn(int timerno, int start, bool up, int fs) {
StartTimer(timerno, start, up, fs);
}
void StopMidiFn() {
StopMidi(); // Stop any currently playing midi
}
/**
* Kill a specific sample, or all samples.
*/
void StopSample(int sample) {
if (sample == -1)
_vm->_sound->stopAllSamples(); // Stop any currently playing sample
else
_vm->_sound->stopSpecSample(sample, 0);
}
/**
* Kill a moving actor's walk.
*/
static void StopWalk(int actor) {
PMOVER pMover;
pMover = GetMover(actor);
assert(pMover);
if (TinselV2) {
if (MoverHidden(pMover))
return;
StopMover(pMover); // Cause the actor to stop
} else {
GetToken(pMover->actorToken); // Kill the walk process
pMover->bStop = true; // Cause the actor to stop
FreeToken(pMover->actorToken);
}
}
/**
* Subtitles on/off
*/
static void Subtitles(int onoff) {
assert (onoff == ST_ON || onoff == ST_OFF);
if (isJapanMode())
return; // Subtitles are always off in JAPAN version (?)
_vm->_config->_useSubtitles = (onoff == ST_ON);
}
/**
* Special walk.
* Walk into or out of a legal path.
*/
static void Swalk(CORO_PARAM, int actor, int x1, int y1, int x2, int y2, SCNHANDLE film, int32 zOverride, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
bool bTookControl; // Set if this function takes control
CORO_END_CONTEXT(_ctx);
HPOLYGON hPath;
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents()) {
if (TinselV2) {
if (x2 == -1 && y2 == -1)
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
else
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0));
}
return;
}
// For lead actor, lock out the user (if not already locked out)
if (actor == GetLeadId() || actor == LEAD_ACTOR) {
_ctx->bTookControl = GetControl(CONTROL_OFFV2);
if (TinselV2 && _ctx->bTookControl)
RestoreMainCursor();
} else {
_ctx->bTookControl = false;
}
if (TinselV2 && (x2 == -1) && (y2 == -1)) {
// First co-ordinates are the destination
x2 = x1;
y2 = y1;
} else {
// Stand at the start co-ordinates
hPath = InPolygon(x1, y1, PATH);
if (hPath != NOPOLY) {
// Walking out of a path
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
} else {
hPath = InPolygon(x2, y2, PATH);
// One of them has to be in a path
assert(hPath != NOPOLY); //one co-ordinate must be in a legal path
// Walking into a path
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x2, y2, 0)); // Get path's characteristics
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x1, y1, 0));
}
if (TinselV2 && (zOverride != -1)) {
PMOVER pMover = GetMover(actor);
assert(pMover);
SetMoverZ(pMover, y1, zOverride);
}
}
CORO_INVOKE_ARGS(Walk, (CORO_SUBCTX, actor, x2, y2, film, 0, true, zOverride, escOn, myEscape));
// Free control if we took it
if (_ctx->bTookControl)
Control(CONTROL_ON);
CORO_END_CODE;
}
/**
* Gets a system variable
*/
static int SystemVar(int varId) {
return SysVar(varId);
}
/**
* Define a tagged actor.
*/
static void TagActor(int actor, SCNHANDLE text, int tp) {
Tag_Actor(actor, text, tp);
}
/**
* TagPos([tag #])
*/
static int TagPos(MASTER_LIB_CODES operand, int tagno, HPOLYGON hp) {
int x, y;
// Tag could be zero, meaning calling tag
if (tagno == 0)
tagno = GetTagPolyId(hp);
if (operand == TAGTAGXPOS || operand == TAGTAGYPOS) {
SCNHANDLE junk;
GetTagTag(GetTagHandle(tagno), &junk, &x, &y);
} else {
GetPolyNode(GetTagHandle(tagno), &x, &y);
}
if (operand == TAGTAGXPOS || operand == TAGWALKXPOS)
return x;
else
return y;
}
/**
* Text goes over actor's head while actor plays the talk reel.
*/
static void FinishTalkingReel(CORO_PARAM, PMOVER pMover, int actor) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (pMover) {
SetMoverStanding(pMover);
AlterMover(pMover, 0, AR_POPREEL);
} else {
SetActorTalking(actor, false);
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, GetActorPlayFilm(actor), -1, -1, 0, false, 0, false, 0, false));
}
CORO_END_CODE;
}
static void TalkOrSay(CORO_PARAM, SPEECH_TYPE speechType, SCNHANDLE hText, int x, int y,
SCNHANDLE hFilm, int actorId, bool bSustain, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int Loffset, Toffset; // Top left of display
int actor; // The speaking actor
PMOVER pActor; // For moving actors
int myLeftEvent;
int escEvents;
int ticks;
bool bTookControl; // Set if this function takes control
bool bTookTags; // Set if this function disables tags
OBJECT *pText; // text object pointer
bool bSample; // Set if a sample is playing
bool bSamples;
bool bTalkReel; // Set while talk reel is playing
Audio::SoundHandle handle;
int timeout;
SPEECH_TYPE whatSort;
TFTYPE direction;
int sub;
int x, y;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->whatSort = speechType;
_ctx->escEvents = myEscape;
_ctx->x = x;
_ctx->y = y;
_ctx->Loffset = 0;
_ctx->Toffset = 0;
_ctx->ticks = 0;
_ctx->pText = NULL;
// If waiting is enabled, wait for ongoing scroll
if (TinselV2 && SysVar(SV_SPEECHWAITS))
CORO_INVOKE_1(WaitScroll, myEscape);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
_ctx->myLeftEvent = GetLeftEvents();
// If this actor is dead, call a stop to the calling process
if (!TinselV2 && (actorId && !actorAlive(actorId)))
CORO_KILL_SELF();
if (!TinselV2 || (speechType == IS_TALK)) {
/*
* Find out which actor is talking
* and with which direction if no film supplied
*/
switch (hFilm) {
case TF_NONE:
case TF_UP:
case TF_DOWN:
case TF_LEFT:
case TF_RIGHT:
_ctx->actor = GetLeadId(); // If no film, actor is lead actor
_ctx->direction = (TFTYPE)hFilm;
break;
default:
_ctx->actor = ExtractActor(hFilm);
assert(_ctx->actor); // talk() - no actor ID in the reel
_ctx->direction = TF_FILM;
break;
}
assert(_ctx->actor);
} else if (TinselV2)
_ctx->actor = actorId;
/*
* Lock out the user (for lead actor, if not already locked out)
* May need to disable tags for other actors
*/
if (_ctx->actor == GetLeadId() || (TinselV2 && (_ctx->actor == LEAD_ACTOR)))
_ctx->bTookControl = GetControl(CONTROL_OFF);
else
_ctx->bTookControl = false;
_ctx->bTookTags = DisableTagsIfEnabled();
if (TinselV2) {
/*
* Divert stuff
*/
if (SysVar(ISV_DIVERT_ACTOR) && (_ctx->actor == GetLeadId() || _ctx->actor == LEAD_ACTOR)) {
_ctx->actor = SysVar(ISV_DIVERT_ACTOR);
if (_ctx->whatSort == IS_TALK)
_ctx->whatSort = IS_SAY;
else if (_ctx->whatSort == IS_TALKAT)
_ctx->whatSort = IS_SAYAT;
}
}
/*
* Kick off the voice sample
*/
if (_vm->_config->_voiceVolume != 0 && _vm->_sound->sampleExists(hText)) {
if (!TinselV2) {
_vm->_sound->playSample(hText, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
_ctx->bSamples = _vm->_mixer->isSoundHandleActive(_ctx->handle);
} else
_ctx->bSamples = true;
} else
_ctx->bSamples = false;
/*
* Replace actor with the talk reel, saving the current one
*/
_ctx->pActor = GetMover(_ctx->actor);
if (_ctx->whatSort == IS_TALK) {
if (_ctx->pActor) {
if (_ctx->direction != TF_FILM)
hFilm = GetMoverTalkReel(_ctx->pActor, _ctx->direction);
AlterMover(_ctx->pActor, hFilm, AR_PUSHREEL);
} else {
SetActorTalking(_ctx->actor, true);
SetActorTalkFilm(_ctx->actor, hFilm);
CORO_INVOKE_ARGS(PlayFilm, (CORO_SUBCTX, hFilm, -1, -1, 0, false, 0, escOn, myEscape, false));
}
_ctx->bTalkReel = true;
CORO_SLEEP(1); // Allow the play to come in
} else if (_ctx->whatSort == IS_TALKAT) {
_ctx->bTalkReel = false;
} else if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
_ctx->bTalkReel = false;
if (IsTaggedActor(_ctx->actor)) {
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, TALKING, false, 0));
} else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY)) {
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, GetTagHandle(_ctx->actor | ACTORTAG_KEY),
TALKING, 0, false, 0));
}
if (TinselV2)
// Let it all kick in and position this 'waiting' process
// down the process list from the playing process(es)
// This ensures immediate return when the reel finishes
CORO_GIVE_WAY;
}
// Make multi-ones escape
if (TinselV2 && (SubStringCount(hText) > 1) && !_ctx->escEvents)
_ctx->escEvents = GetEscEvents();
for (_ctx->sub = 0; _ctx->sub < (TinselV2 ? SubStringCount(hText) : 1); _ctx->sub++) {
if (TinselV2 && _ctx->escEvents && _ctx->escEvents != GetEscEvents())
break;
/*
* Display the text.
*/
_ctx->bSample = _ctx->bSamples;
_ctx->pText = NULL;
if (isJapanMode()) {
_ctx->ticks = JAP_TEXT_TIME;
} else if (_vm->_config->_useSubtitles || !_ctx->bSample) {
/*
* Work out where to display the text
*/
int xshift, yshift;
PlayfieldGetPos(FIELD_WORLD, &_ctx->Loffset, &_ctx->Toffset);
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_TALK))
GetActorMidTop(_ctx->actor, &_ctx->x, &_ctx->y);
if (!TinselV0)
SetTextPal(GetActorRGB(_ctx->actor));
if (TinselV2)
LoadSubString(hText, _ctx->sub, TextBufferAddr(), TBUFSZ);
else {
LoadStringRes(hText, TextBufferAddr(), TBUFSZ);
_ctx->y -= _ctx->Toffset;
}
_ctx->pText = ObjectTextOut(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());
}
if (TinselV2 && _ctx->bSample) {
// Kick off the sample now (perhaps with a delay)
if (bNoPause)
bNoPause = false;
else if (!IsDemo)
CORO_SLEEP(SysVar(SV_SPEECHDELAY));
//SamplePlay(VOICE, hText, _ctx->sub, false, -1, -1, PRIORITY_TALK);
_vm->_sound->playSample(hText, _ctx->sub, false, -1, -1, PRIORITY_TALK, Audio::Mixer::kSpeechSoundType, &_ctx->handle);
}
_ctx->timeout = SAMPLETIMEOUT;
do {
// Keep text in place if scrolling
if (_ctx->pText != NULL) {
int nLoff, nToff;
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != _ctx->Loffset || nToff != _ctx->Toffset) {
MultiMoveRelXY(_ctx->pText, _ctx->Loffset - nLoff, _ctx->Toffset - nToff);
_ctx->Loffset = nLoff;
_ctx->Toffset = nToff;
}
}
CORO_SLEEP(1);
// Handle timeout decrementing and Escape presses
if (TinselV2) {
if ((_ctx->escEvents && _ctx->escEvents != GetEscEvents()) ||
(!bSustain && LeftEventChange(_ctx->myLeftEvent)) ||
(--_ctx->timeout <= 0)) {
// Left event only kills current sub-string
_ctx->myLeftEvent = GetLeftEvents();
break;
}
} else {
--_ctx->timeout;
// Abort if escapable and ESCAPE is pressed
// Abort if left click - hardwired feature for talk!
// Abort if sample times out
if ((escOn && myEscape != GetEscEvents())
|| (_ctx->myLeftEvent != GetLeftEvents())
|| (_ctx->timeout <= 0))
break;
}
if (_ctx->bSample) {
// Wait for sample to end whether or not
if (!_vm->_mixer->isSoundHandleActive(_ctx->handle)) {
if (_ctx->pText == NULL || _vm->_config->_textSpeed == DEFTEXTSPEED) {
// No text or speed modification - just depends on sample
break;
} else {
// Talk reel stops at end of speech
if (!TinselV2 || (_ctx->bTalkReel && (_ctx->sub == SubStringCount(hText) - 1))) {
CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
_ctx->bTalkReel = false;
}
_ctx->bSample = false;
}
}
// Decrement the subtitles timeout counter
if (_ctx->ticks > 0) --_ctx->ticks;
} else {
// No sample - just depends on time
if (_ctx->ticks-- <= 0)
break;
}
} while (1);
if (_ctx->pText != NULL) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL;
}
if (TinselV2 && _ctx->bSample)
_vm->_sound->stopSpecSample(hText, _ctx->sub);
}
/*
* The talk is over now - dump the text
* Stop the sample
* Restore the actor's film or standing reel
*/
if (_ctx->bTalkReel)
CORO_INVOKE_2(FinishTalkingReel, _ctx->pActor, _ctx->actor);
if (_ctx->pText != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
if (TinselV2) {
if ((_ctx->whatSort == IS_SAY) || (_ctx->whatSort == IS_SAYAT)) {
SetActorTalking(_ctx->actor, false);
if (IsTaggedActor(_ctx->actor))
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->actor, ENDTALK, false, 0));
else if (IsTagPolygon(_ctx->actor | ACTORTAG_KEY))
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX,
GetTagHandle(_ctx->actor | ACTORTAG_KEY), ENDTALK, 0, false, 0));
CORO_SLEEP(1);
}
} else {
_vm->_mixer->stopHandle(_ctx->handle);
}
/*
* Restore user control and tags, as appropriate
* And, finally, release the talk token.
*/
if (_ctx->bTookControl) {
if (TinselV2) ControlOn(); else Control(CONTROL_ON);
}
if (_ctx->bTookTags)
EnableTags();
CORO_END_CODE;
}
/**
* TalkAt(actor, x, y, text)
*/
static void TalkAt(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, bool escOn, int myEscape) {
if (!coroParam) {
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
if (!isJapanMode() && (_vm->_config->_useSubtitles || !_vm->_sound->sampleExists(text)))
SetTextPal(GetActorRGB(actor));
}
Print(coroParam, x, y, text, 0, false, escOn, myEscape);
}
/**
* TalkAtS(actor, x, y, text, sustain)
*/
static void TalkAtS(CORO_PARAM, int actor, int x, int y, SCNHANDLE text, int sustain, bool escOn, int myEscape) {
if (!coroParam) {
assert(sustain == 2);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
if (!isJapanMode())
SetTextPal(GetActorRGB(actor));
}
Print(coroParam, x, y, text, 0, sustain == 2, escOn, myEscape);
}
/**
* Set talk font's palette entry.
*/
static void TalkAttr(int r1, int g1, int b1, bool escOn, int myEscape) {
if (isJapanMode())
return;
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
if (r1 > MAX_INTENSITY) r1 = MAX_INTENSITY; // } Ensure
if (g1 > MAX_INTENSITY) g1 = MAX_INTENSITY; // } within limits
if (b1 > MAX_INTENSITY) b1 = MAX_INTENSITY; // }
SetTextPal(TINSEL_RGB(r1, g1, b1));
}
/**
* TalkPaletteIndex
*/
static void TalkPaletteIndex(unsigned index) {
assert(index);
SetTalkTextOffset(index);
}
/**
* Set talk font's palette entry.
*/
static void TalkRGB(COLORREF 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.
*/
static int TimerFn(int timerno) {
return Timer(timerno);
}
/**
* Return the icon that caused the CONVERSE event.
*/
int Topic() {
return GetIcon();
}
/**
* topplay(film, x, y, actor, hold, complete)
*/
static void TopPlay(CORO_PARAM, SCNHANDLE film, int x, int y, int complete, int actorid, bool splay, int sfact, bool escOn, int myescTime) {
Play(coroParam, film, x, y, complete, actorid, splay, sfact, escOn, myescTime, true);
}
static void TopPlay(CORO_PARAM, SCNHANDLE hFilm, int x, int y, bool bComplete, int myescEvent, TINSEL_EVENT event) {
Play(coroParam, hFilm, x, y, bComplete, myescEvent, true, event, NOPOLY, 0);
}
/**
* Open or close the 'top window'
*/
static void TopWindow(int bpos) {
bool isStart = (TinselV2 && (bpos != 0)) || (!TinselV2 && (bpos == TW_START));
KillInventory();
if (isStart)
OpenMenu(TOP_WINDOW);
}
/**
* TranslucentIndex
*/
static void TranslucentIndex(unsigned index) {
assert(index <= 255);
SetTranslucencyOffset(index);
}
/**
* Play a sample.
*/
static void TryPlaySample(CORO_PARAM, int sample, bool bComplete, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not appropriate
if (_vm->_sound->sampleIsPlaying()) {
// return, but prevent Glitter lock-up
CORO_SLEEP(1);
return;
}
CORO_INVOKE_ARGS(PlaySample, (CORO_SUBCTX, sample, bComplete, escOn, myEscape));
CORO_END_CODE;
}
/**
* UnDimMusic
*/
static void UnDimMusic() {
_vm->_pcmMusic->unDim(true);
}
/**
* unhookscene
*/
static void UnHookSceneFn() {
UnHookScene();
}
/**
* Un-define an actor as tagged.
*/
static void UnTagActorFn(int actor) {
UnTagActor(actor);
}
/**
* vibrate
*/
static void Vibrate() {
}
/**
* waitframe(int actor, int frameNumber)
*/
static void WaitFrame(CORO_PARAM, int actor, int frameNumber, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (GetActorSteps(actor) < frameNumber) {
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
break;
CORO_SLEEP(1);
}
CORO_END_CODE;
}
/**
* Return when a key pressed or button pushed.
*/
static void WaitKey(CORO_PARAM, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int startEvent;
int startX, startY;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
for (;;) {
_ctx->startEvent = getUserEvents();
if (TinselV1) {
// Store cursor position
while (!GetCursorXYNoWait(&_ctx->startX, &_ctx->startY, false))
CORO_SLEEP(1);
}
while (_ctx->startEvent == getUserEvents()) {
CORO_SLEEP(1);
// Not necessary to monitor escape as it's an event anyway
if (TinselV1) {
int curX, curY;
GetCursorXY(&curX, &curY, false); // Store cursor position
if (curX != _ctx->startX || curY != _ctx->startY)
break;
}
if (MenuActive())
break;
}
if (!MenuActive())
return;
do {
CORO_SLEEP(1);
} while (MenuActive());
CORO_SLEEP(ONE_SECOND / 2); // Let it die down
}
CORO_END_CODE;
}
/**
* Return when no scrolling is going on.
*/
void WaitScroll(CORO_PARAM, int myescEvent) {
CORO_BEGIN_CONTEXT;
int time;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// wait for ongoing scroll
while (IsScrolling()) {
if (myescEvent && myescEvent != GetEscEvents())
break;
CORO_SLEEP(1);
}
CORO_END_CODE;
}
/**
* Pause for requested time.
*/
static void WaitTime(CORO_PARAM, int time, bool frame, bool escOn, int myEscape) {
CORO_BEGIN_CONTEXT;
int time;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Don't do it if it's not wanted
if (escOn && myEscape != GetEscEvents())
return;
if (!frame)
time *= ONE_SECOND;
_ctx->time = time;
do {
CORO_SLEEP(1);
// Abort if escapable and ESCAPE is pressed
if (escOn && myEscape != GetEscEvents())
break;
} while (_ctx->time--);
CORO_END_CODE;
}
/**
* Set a moving actor off on a walk.
*/
void Walk(CORO_PARAM, int actor, int x, int y, SCNHANDLE hFilm, int hold, bool igPath,
int zOverride, bool escOn, int myescEvent) {
CORO_BEGIN_CONTEXT;
int thisWalk;
CORO_END_CONTEXT(_ctx);
bool bQuick = hold != 0;
PMOVER pMover = GetMover(actor);
assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
// Straight there if escaped
if (escOn && myescEvent != GetEscEvents()) {
if (TinselV2)
StopMover(pMover);
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
return;
}
if (TinselV2) {
if (MoverHidden(pMover))
return;
// Test 10/10/96
while (!MoverIs(pMover))
CORO_SLEEP(1);
}
assert(pMover->hCpath != NOPOLY); // moving actor not in path
// Croak if he is doing an SWalk()
if (TinselV2) {
// Croak if he is doing an SWalk()
if (MoverIsSWalking(pMover))
CORO_KILL_SELF();
_ctx->thisWalk = SetActorDest(pMover, x, y, igPath, hFilm);
SetMoverZoverride(pMover, zOverride);
DontScrollCursor();
if (!bQuick) {
while (MoverMoving(pMover)) {
// Straight there if escaped
if (escOn && myescEvent != GetEscEvents()) {
StopMover(pMover);
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
break;
}
CORO_SLEEP(1);
// Die if superceded
if (_ctx->thisWalk != GetWalkNumber(pMover))
CORO_KILL_SELF();
}
}
} else {
GetToken(pMover->actorToken);
SetActorDest(pMover, x, y, igPath, hFilm);
DontScrollCursor();
if (hold == 2) {
;
} else {
while (MoverMoving(pMover)) {
CORO_SLEEP(1);
// Straight there if escaped
if (escOn && myescEvent != GetEscEvents()) {
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
FreeToken(pMover->actorToken);
return;
}
}
}
FreeToken(pMover->actorToken);
}
CORO_END_CODE;
}
/**
* Set a moving actor off on a walk.
* Wait to see if its aborted or completed.
*/
static void Walked(CORO_PARAM, int actor, int x, int y, SCNHANDLE film, bool escOn, int myEscape, bool &retVal) {
// COROUTINE
CORO_BEGIN_CONTEXT;
int thisWalk;
CORO_END_CONTEXT(_ctx);
PMOVER pMover = GetMover(actor);
assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
// Straight there if escaped
if (escOn && myEscape != GetEscEvents()) {
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
retVal = true;
return;
}
if (TinselV2) {
if (MoverHidden(pMover) || !MoverIs(pMover)) {
retVal = false;
return;
}
assert(pMover->hCpath != NOPOLY); // moving actor not in path
// Not if he is doing an SWalk()
if (MoverIsSWalking(pMover)) {
retVal = false;
return;
}
} else {
// Pause before starting the walk
CORO_SLEEP(ONE_SECOND);
assert(pMover->hCpath != NOPOLY); // moving actor not in path
// Briefly aquire token to kill off any other normal walk
GetToken(pMover->actorToken);
FreeToken(pMover->actorToken);
}
_ctx->thisWalk = SetActorDest(pMover, x, y, false, film);
DontScrollCursor();
while (MoverMoving(pMover) && (_ctx->thisWalk == GetWalkNumber(pMover))) {
// Straight there if escaped
if (escOn && myEscape != GetEscEvents()) {
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, actor, x, y, 0));
retVal = true;
return;
}
CORO_SLEEP(1);
}
int endx, endy;
GetMoverPosition(pMover, &endx, &endy);
retVal = (_ctx->thisWalk == GetWalkNumber(pMover) && endx == x && endy == y);
CORO_END_CODE;
}
/**
* Declare a moving actor.
*/
static void WalkingActor(uint32 id, SCNHANDLE *rp = NULL) {
PMOVER pActor; // Moving actor structure
if (TinselVersion == TINSEL_V2) {
RegisterMover(id);
return;
}
RegisterMover(id); // Establish as a moving actor
pActor = GetMover(id);
assert(pActor);
// Store all those reels
int i, j;
for (i = 0; i < 5; ++i) {
for (j = 0; j < 4; ++j)
pActor->walkReels[i][j] = *rp++;
for (j = 0; j < 4; ++j)
pActor->standReels[i][j] = *rp++;
}
for (i = NUM_MAINSCALES; i < TOTAL_SCALES; i++) {
for (j = 0; j < 4; ++j) {
pActor->walkReels[i][j] = pActor->walkReels[4][j];
pActor->standReels[i][j] = pActor->standReels[2][j];
}
}
}
/**
* Walk a moving actor towards the polygon's tag, but return when the
* actor enters the polygon.
*/
static void WalkPoly(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
int pnodex, pnodey;
// COROUTINE
CORO_BEGIN_CONTEXT;
int thisWalk;
CORO_END_CONTEXT(_ctx);
assert(hp != NOPOLY); // WalkPoly() may only be called from a polygon code block
PMOVER pMover = GetMover(actor);
assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
// Straight there if escaped
if (escOn && myEscape != GetEscEvents()) {
StandTag(actor, hp);
return;
}
if (TinselV2) {
if (MoverHidden(pMover))
return;
// Croak if he is doing an SWalk()
if (MoverIsSWalking(pMover))
CORO_KILL_SELF();
} else {
GetToken(pMover->actorToken);
}
GetPolyNode(hp, &pnodex, &pnodey);
_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
DoScrollCursor();
while (!MoverIsInPolygon(pMover, hp) && MoverMoving(pMover)) {
CORO_SLEEP(1);
if (escOn && myEscape != GetEscEvents()) {
// Straight there if escaped
StandTag(actor, hp);
if (!TinselV2)
FreeToken(pMover->actorToken);
return;
}
// Die if superceded
if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
CORO_KILL_SELF();
}
if (!TinselV2)
FreeToken(pMover->actorToken);
CORO_END_CODE;
}
/**
* WalkTag(actor, reel, hold)
*/
static void WalkTag(CORO_PARAM, int actor, SCNHANDLE film, HPOLYGON hp, bool escOn, int myEscape) {
// COROUTINE
CORO_BEGIN_CONTEXT;
int thisWalk;
CORO_END_CONTEXT(_ctx);
PMOVER pMover = GetMover(actor);
assert(pMover); // Can't walk a non-moving actor
CORO_BEGIN_CODE(_ctx);
int pnodex, pnodey;
assert(hp != NOPOLY); // walkpoly() may only be called from a polygon code block
// Straight there if escaped
if (escOn && myEscape != GetEscEvents()) {
StandTag(actor, hp);
return;
}
if (!TinselV2)
GetToken(pMover->actorToken);
else {
if (MoverHidden(pMover))
return;
}
GetPolyNode(hp, &pnodex, &pnodey);
_ctx->thisWalk = SetActorDest(pMover, pnodex, pnodey, false, film);
DoScrollCursor();
while (MoverMoving(pMover)) {
if (escOn && myEscape != GetEscEvents()) {
// Straight there if escaped
StandTag(actor, hp);
if (!TinselV2)
FreeToken(pMover->actorToken);
return;
}
CORO_SLEEP(1);
// Die if superceded
if (TinselV2 && (_ctx->thisWalk != GetWalkNumber(pMover)))
CORO_KILL_SELF();
}
// Adopt the tag-related reel
SCNHANDLE pFilm = GetPolyFilm(hp);
switch (pFilm) {
case TF_NONE:
break;
case TF_UP:
SetMoverDirection(pMover, AWAY);
SetMoverStanding(pMover);
break;
case TF_DOWN:
SetMoverDirection(pMover, FORWARD);
SetMoverStanding(pMover);
break;
case TF_LEFT:
SetMoverDirection(pMover, LEFTREEL);
SetMoverStanding(pMover);
break;
case TF_RIGHT:
SetMoverDirection(pMover, RIGHTREEL);
SetMoverStanding(pMover);
break;
default:
if (actor == LEAD_ACTOR || actor == GetLeadId())
AlterMover(pMover, pFilm, AR_NORMAL);
else
SetMoverStanding(pMover);
break;
}
if (!TinselV2)
FreeToken(pMover->actorToken);
CORO_END_CODE;
}
/**
* Returns the X co-ordinateof lead actor's last walk.
*/
int WalkXPos() {
return GetLastLeadXdest();
}
/**
* Returns the Y co-ordinateof lead actor's last walk.
*/
int WalkYPos() {
return GetLastLeadYdest();
}
/**
* Return which is the current CD, counting from 1.
*/
int WhichCd() {
return GetCurrentCD();
}
/**
* whichinventory
*/
int WhichInventory() {
return WhichInventoryOpen();
}
/**
* Subtract one less that the number of parameters from pp
* pp then points to the first parameter.
*
* If the library function has no return value:
* return -(the number of parameters) to pop them from the stack
*
* If the library function has a return value:
* return -(the number of parameters - 1) to pop most of them from
* the stack, and stick the return value in pp[0]
* @param operand Library function
* @param pp Top of parameter stack
*/
int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState) {
int libCode;
if (TinselV0) libCode = DW1DEMO_CODES[operand];
else if (!TinselV2) libCode = DW1_CODES[operand];
else if (_vm->getFeatures() & GF_DEMO) libCode = DW2DEMO_CODES[operand];
else libCode = DW2_CODES[operand];
debug(7, "CallLibraryRoutine op %d (escOn %d, myEscape %d)", operand, pic->escOn, pic->myEscape);
switch (libCode) {
case ACTORATTR:
// DW1 only
pp -= 3; // 4 parameters
ActorAttr(pp[0], pp[1], pp[2], pp[3]);
return -4;
case ACTORBRIGHTNESS:
// DW2 only
pp -= 1;
ActorBrightness(pp[0], pp[1]);
return -2;
case ACTORDIRECTION:
// Common to both DW1 & DW2
pp[0] = ActorDirection(pp[0]);
return 0;
case ACTORPALETTE:
// DW2 only
pp -= 2; // 3 parameters
ActorPalette(pp[0], pp[1], pp[2]);
return -3;
case ACTORPRIORITY:
// DW2 only
pp -= 1; // 2 parameters
ActorPriority(pp[0], pp[1]);
return -2;
case ACTORREF:
// Common to both DW1 & DW2
if (!TinselV0)
error("actorref isn't a real function");
return 0;
case ACTORRGB:
// DW2 only
pp -= 1; // 2 parameters
ActorRGB(pp[0], pp[1]);
return -2;
case ACTORSCALE:
// Common to both DW1 & DW2
pp[0] = ActorScale(pp[0]);
return 0;
case ACTORSON:
// DW1 only
ActorsOn();
return 0;
case ACTORXPOS:
// Common to both DW1 & DW2
pp[0] = ActorPos(ACTORXPOS, pp[0]);
return 0;
case ACTORYPOS:
// Common to both DW1 & DW2
pp[0] = ActorPos(ACTORYPOS, pp[0]);
return 0;
case ADDHIGHLIGHT:
// DW2 only
// Command doesn't actually do anything
pp -= 1; // 2 parameters
return -2;
case ADDINV:
// DW2 only
AddInv(INV_DEFAULT, pp[0]);
return -1;
case ADDINV1:
// Common to both DW1 & DW2
AddInv(INV_1, pp[0]);
return -1;
case ADDINV2:
// Common to both DW1 & DW2
AddInv(INV_2, pp[0]);
return -1;
case ADDOPENINV:
// Common to both DW1 & DW2
AddInv(TinselV2 ? DW2_INV_OPEN : INV_OPEN, pp[0]);
return -1;
case ADDTOPIC:
// Common to both DW1 & DW2
AddTopic(pp[0]);
return -1;
case AUXSCALE:
// DW1 only
pp -= 13; // 14 parameters
AuxScale(pp[0], pp[1], (SCNHANDLE *)(pp+2));
return -14;
case BACKGROUND:
// Common to both DW1 & DW2
Background(coroParam, pp[0]);
return -1;
case BLOCKING:
// DW2 only
Blocking(pp[0]);
return -1;
case CALLACTOR:
case CALLGLOBALPROCESS:
case CALLOBJECT:
// DW2 only
pp -= 1; // 2 parameters
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
bool result;
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic, &result);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
pp[0] = result ? 1 : 0;
} else {
uint32 v;
if (libCode == CALLACTOR)
v = SendActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
else if (libCode == CALLGLOBALPROCESS)
v = SendGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
else
v = SendObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
if (coroParam)
return 0;
pp[0] = v;
}
if (!pp[0])
KillSelf(coroParam);
return -2;
case CALLPROCESS:
// DW2 only
pp -= 1; // 2 parameters
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
bool result;
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic, &result);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
pp[0] = result ? 1 : 0;
} else {
int result = SendProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
if (coroParam)
return 0;
pp[0] = result;
}
return -2;
case CALLSCENE:
// DW2 only
error("CallScene isn't a real function");
case CALLTAG:
// DW2 only
pp -= 1; // 2 parameters
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
bool result;
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic, &result);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
pp[0] = result ? 1 : 0;
} else {
bool result;
SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
if (coroParam)
return 0;
pp[0] = result ? 1 : 0;
}
if (!pp[0])
KillSelf(coroParam);
return -2;
case CAMERA:
// Common to both DW1 & DW2
Camera(pp[0]);
return -1;
case CDCHANGESCENE:
// DW2 only
CdChangeScene(pp[0]);
return -1;
case CDDOCHANGE:
// DW2 only
CdDoChange(coroParam);
return 0;
case CDENDACTOR:
// DW2 only
CdEndActor(pp[0], pic->myEscape);
return -1;
case CDLOAD:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
CDload(pp[0], pp[1], pic->myEscape);
return -2;
case CDPLAY:
// Common to both DW1 & DW2
error("cdplay isn't a real function");
case CLEARHOOKSCENE:
// Common to both DW1 & DW2
ClearHookScene();
return 0;
case CLOSEINVENTORY:
// Common to both DW1 & DW2
CloseInventory();
return 0;
case CONTROL:
// Common to both DW1 & DW2
Control(pp[0]);
return -1;
case CONVERSATION:
// Common to both DW1 & DW2
Conversation(coroParam, pp[0], pic->hPoly, pic->idActor, pic->escOn, pic->myEscape);
return -1;
case CONVTOPIC:
// Common to both DW1 & DW2
ConvTopic(pp[0]);
return -1;
case CURSOR:
// DW2 only
Cursor(pp[0]);
return -1;
case CURSORXPOS:
// Common to both DW1 & DW2
pp[0] = CursorPos(CURSORXPOS);
return 0;
case CURSORYPOS:
// Common to both DW1 & DW2
pp[0] = CursorPos(CURSORYPOS);
return 0;
case CUTSCENE:
// DW1 only
error("cutscene isn't a real function");
case DECCONVW:
// Common to both DW1 & DW2
pp -= 7; // 8 parameters
DecConvW(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
case DECCSTRINGS:
// DW1 only
pp -= 19; // 20 parameters
DecCStrings((SCNHANDLE *)pp);
return -20;
case DECCURSOR:
// Common to both DW1 & DW2
DecCursor(pp[0]);
return -1;
case DECFLAGS:
// Common to both DW1 & DW2
if (TinselV2)
error("DecFlags() is obsolete");
DecFlags(pp[0]);
return -1;
case DECINV1:
// Common to both DW1 & DW2
pp -= 7; // 8 parameters
DecInv1(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
case DECINV2:
// Common to both DW1 & DW2
pp -= 7; // 8 parameters
DecInv2(pp[0], pp[1], pp[2], pp[3],
pp[4], pp[5], pp[6], pp[7]);
return -8;
case DECINVW:
// Common to both DW1 & DW2
DecInvW(pp[0]);
return -1;
case DECLARELANGUAGE:
// DW2 only
pp -= 2; // 3 parameters
DeclareLanguage(pp[0], pp[1], pp[2]);
return -3;
case DECLEAD:
// Common to both DW1 & DW2
if (TinselV2) {
DecLead(pp[0]);
return -1;
} else {
pp -= 61; // 62 parameters
DecLead(pp[0], (SCNHANDLE *)&pp[1], pp[61]);
return -62;
}
case DECSCALE:
// DW2 only
pp -= 13; // 14 parameters
DecScale(pp[0], pp[1], pp[2], pp[3], pp[4],
pp[5], pp[6], pp[7], pp[8], pp[9],
pp[10], pp[11], pp[12], pp[13]);
return -14;
case DECTAGFONT:
// Common to both DW1 & DW2
DecTagFont(pp[0]);
return -1;
case DECTALKFONT:
// Common to both DW1 & DW2
DecTalkFont(pp[0]);
return -1;
case DELICON:
// DW1 only
DelIcon(pp[0]);
return -1;
case DELINV:
// DW1 only
DelInv(pp[0]);
return -1;
case DELTOPIC:
// DW2 only
DelTopic(pp[0]);
return -1;
case DIMMUSIC:
// DW2 only
DimMusic();
return 0;
case DROP:
// DW2 only
Drop(pp[0]);
return -1;
case DROPEVERYTHING:
// DW2 only
DropEverything();
return 0;
case DROPOUT:
// DW1 only
error("DropOut (%d)", pp[0]);
case EFFECTACTOR:
// Common to both DW1 & DW2
assert(pic->event == WALKIN || pic->event == WALKOUT); // effectactor() must be from effect poly code
pp[0] = pic->idActor;
return 0;
case ENABLEMENU:
// Common to both DW1 & DW2
EnableMenu();
return 0;
case ENDACTOR:
// DW2 only
EndActor(pp[0]);
return -1;
case ESCAPE:
case ESCAPEOFF:
case ESCAPEON:
// Common to both DW1 & DW2
error("Escape isn't a real function");
case EVENT:
// Common to both DW1 & DW2
if (TinselVersion == TINSEL_V2)
pp[0] = pic->event;
else
pp[0] = TINSEL1_EVENT_MAP[pic->event];
return 0;
case FACETAG:
// DW2 only
FaceTag(pp[0], pic->hPoly);
return -1;
case FADEIN:
// DW2 only
FadeIn();
return 0;
case FADEMIDI:
// DW1 only
FadeMidi(coroParam, pp[0]);
return -1;
case FADEOUT:
// DW1 only
FadeOut();
return 0;
case FRAMEGRAB:
// Common to both DW1 & DW2
return -1;
case FREEZECURSOR:
// DW2 only
FreezeCursor(pp[0]);
return -1;
case GETINVLIMIT:
// Common to both DW1 & DW2
pp[0] = GetInvLimit(pp[0]);
return 0;
case GHOST:
// DW2 only
pp -= 2; // 3 parameters
Ghost(pp[0], pp[1], pp[2]);
return -3;
case GLOBALVAR:
// DW1 only
error("GlobalVar isn't a real function");
case GRABMOVIE:
// DW2 only
return -1;
case HAILSCENE:
// DW2 only
HailScene(pp[0]);
return -1;
case HASRESTARTED:
// Common to both DW1 & DW2
pp[0] = HasRestarted();
return 0;
case HAVE:
// DW2 only
pp[0] = Have(pp[0]);
return 0; // using return value
case HELDOBJECT:
// Common to both DW1 & DW2
pp[0] = HeldObject();
return 0;
case HIDEACTOR:
// Common to both DW1 & DW2
if (!TinselV2)
HideActorFn(coroParam, pp[0]);
else if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
} else
HideActorFn(coroParam, pp[0]);
return -1;
case HIDEBLOCK:
// DW2 only
HideBlock(pp[0]);
return -1;
case HIDEEFFECT:
// DW2 only
HideEffect(pp[0]);
return -1;
case HIDEPATH:
// DW2 only
HidePath(pp[0]);
return -1;
case HIDEREFER:
// DW2 only
HideRefer(pp[0]);
return -1;
case HIDETAG:
// DW2 only
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
} else {
HideTag(coroParam, pp[0], pic->hPoly);
if (coroParam)
return 0;
}
return -1;
case HOLD:
// DW2 only
Hold(pp[0]);
return -1;
case HOOKSCENE:
// Common to both DW1 & DW2
pp -= 2; // 3 parameters
HookScene(pp[0], pp[1], pp[2]);
return -3;
case IDLETIME:
// Common to both DW1 & DW2
pp[0] = IdleTime();
return 0;
case ININVENTORY:
// DW1 only
pp[0] = InInventory(pp[0]);
return 0; // using return value
case INSTANTSCROLL:
// DW2 only
InstantScroll(pp[0]);
return -1;
case INVDEPICT:
// DW1 only
pp -= 1; // 2 parameters
InvDepict(pp[0], pp[1]);
return -2;
case INVENTORY:
// Common to both DW1 & DW2
Inventory(pp[0], pic->escOn, pic->myEscape);
return -1;
case INVPLAY:
// DW2 only
pp -= 1; // 2 parameters
InvPlay(pp[0], pp[1]);
return -2;
case INWHICHINV:
// Common to both DW1 & DW2
pp[0] = InWhichInv(pp[0]);
return 0; // using return value
case KILLACTOR:
// DW1 only
if (TinselV2)
error("KillActor() was not expected to be required");
KillActor(pp[0]);
return -1;
case KILLBLOCK:
// DW1 only
KillBlock(pp[0]);
return -1;
case KILLEXIT:
// DW1 only
KillExit(pp[0]);
return -1;
case KILLGLOBALPROCESS:
// DW2 only
KillGlobalProcess(pp[0]);
return -1;
case KILLPROCESS:
// DW2 only
KillProcess(pp[0]);
return -1;
case KILLTAG:
// DW1 only
KillTag(coroParam, pp[0]);
return -1;
case LOCALVAR:
// DW2 only
error("LocalVar isn't a real function");
case MOVECURSOR:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
MoveCursor(pp[0], pp[1]);
return -2;
case MOVETAG:
// DW2 only
pp -= 2; // 3 parameters
MoveTag(pp[0], pp[1], pp[2], pic->hPoly);
return -3;
case MOVETAGTO:
// DW2 only
pp -= 2; // 3 parameters
MoveTagTo(pp[0], pp[1], pp[2], pic->hPoly);
return -3;
case NEWSCENE:
// Common to both DW1 & DW2
pp -= 2; // 3 parameters
if (*pResumeState == RES_2)
*pResumeState = RES_NOT;
else
NewScene(coroParam, pp[0], pp[1], pp[2]);
return -3;
case NOBLOCKING:
// Common to both DW1 & DW2
NoBlocking();
return 0;
case NOPAUSE:
// DW2 only
bNoPause = true;
return 0;
case NOSCROLL:
// Common to both DW1 & DW2
pp -= 3; // 4 parameters
NoScroll(pp[0], pp[1], pp[2], pp[3]);
return -4;
case OBJECTHELD:
// DW1 only
ObjectHeld(pp[0]);
return -1;
case OFFSET:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 2; // 2 parameters
Offset((EXTREME)pp[0], pp[1], pp[2]);
return -3;
} else {
pp -= 1; // 2 parameters
Offset(EX_USEXY, pp[0], pp[1]);
return -2;
}
case OTHEROBJECT:
// DW2 only
pp[0] = OtherObject(pic->pinvo);
return 0;
case PAUSE:
// DW2 only
WaitTime(coroParam, 1, true, pic->escOn, pic->myEscape);
return 0;
case PLAY:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 3; // 4 parameters
if (*pResumeState == RES_1 && IsCdPlayHandle(pp[0]))
*pResumeState = RES_NOT;
else {
Play(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, false,
pic->event, pic->hPoly, pic->idActor);
if (coroParam)
return 0;
}
return -4;
} else {
pp -= 5; // 6 parameters
if (pic->event == WALKIN || pic->event == WALKOUT)
Play(coroParam, pp[0], pp[1], pp[2], pp[5], 0, false, 0, pic->escOn, pic->myEscape, false);
else
Play(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape, false);
return -6;
}
case PLAYMIDI:
// Common to both DW1 & DW2
pp -= 2; // 3 parameters
PlayMidi(coroParam, pp[0], pp[1], pp[2]);
return -3;
case PLAYMOVIE:
// DW2 only
PlayMovie(coroParam, pp[0], pic->myEscape);
return -1;
case PLAYMUSIC:
// DW2 only
PlayMusic(pp[0]);
return -1;
case PLAYRTF:
// Common to both DW1 & DW2
error("playrtf only applies to cdi");
case PLAYSAMPLE:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 3; // 4 parameters
PlaySample(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape);
return -4;
} else {
pp -= 1; // 2 parameters
PlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
}
case POINTACTOR:
// DW2 only
PointActor(pp[0]);
return -1;
case POINTTAG:
// DW2 only
PointTag(pp[0], pic->hPoly);
return -1;
case POSTACTOR:
// DW2 only
pp -= 1; // 2 parameters
PostActor(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->idActor, pic->myEscape);
return -2;
case POSTGLOBALPROCESS:
// DW2 only
pp -= 1; // 2 parameters
PostGlobalProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
return -2;
case POSTOBJECT:
// DW2 only
pp -= 1; // 2 parameters
PostObject(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
return -2;
case POSTPROCESS:
// DW2 only
pp -= 1; // 2 parameters
PostProcess(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->myEscape);
return -2;
case POSTTAG:
// DW2 only
pp -= 1; // 2 parameters
PostTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape);
return -2;
case PREPARESCENE:
// DW1 only
PrepareScene(pp[0]);
return -1;
case PRINT:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 4; // 5 parameters
Print(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4] != 0, pic->escOn, pic->myEscape);
return -5;
} else {
pp -= 5; // 6 parameters
/* pp[2] was intended to be attribute */
Print(coroParam, pp[0], pp[1], pp[3], pp[4], pp[5] == 2, pic->escOn, pic->myEscape);
return -6;
}
case PRINTCURSOR:
// DW2 only
PrintTag(pic->hPoly, pp[0], pic->idActor, true);
return -1;
case PRINTOBJ:
// Common to both DW1 & DW2
PrintObj(coroParam, pp[0], pic->pinvo, pic->event, pic->myEscape);
return -1;
case PRINTTAG:
// Common to both DW1 & DW2
PrintTag(pic->hPoly, pp[0], TinselV2 ? pic->idActor : 0, false);
return -1;
case QUITGAME:
// Common to both DW1 & DW2
QuitGame();
return 0;
case RANDOM:
// Common to both DW1 & DW2
pp -= 2; // 3 parameters
pp[0] = RandomFn(pp[0], pp[1], pp[2]);
return -2; // One holds return value
case RESETIDLETIME:
// Common to both DW1 & DW2
ResetIdleTime();
return 0;
case RESTARTGAME:
// Common to both DW1 & DW2
FnRestartGame();
return 0;
case RESTORESCENE:
// Common to both DW1 & DW2
if (TinselV2) {
RestoreScene(coroParam, (TRANSITS)pp[0]);
return -1;
} else {
RestoreScene(coroParam, TRANS_FADE);
return 0;
}
case RESTORE_CUT:
// DW1 only
RestoreScene(coroParam, TRANS_CUT);
return 0;
case RESUMELASTGAME:
// DW2 only
ResumeLastGame();
return 0;
case RUNMODE:
// Common to both DW1 & DW2
pp[0] = RunMode();
return 0;
case SAMPLEPLAYING:
// DW1 only
pp[0] = SamplePlaying(pic->escOn, pic->myEscape);
return 0;
case SAVESCENE:
// Common to both DW1 & DW2
if (*pResumeState == RES_1)
*pResumeState = RES_2;
else
SaveScene(coroParam);
return 0;
case SAY:
// DW2 only
pp -= 1; // 2 parameters
TalkOrSay(coroParam, IS_SAY, pp[1], 0, 0, 0, pp[0], false, pic->escOn, pic->myEscape);
return -2;
case SAYAT:
// DW2 only
pp -= 4; // 5 parameters
TalkOrSay(coroParam, IS_SAYAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
return -5;
case SCALINGREELS:
// Common to both DW1 & DW2
pp -= 6; // 7 parameters
ScalingReels(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
return -7;
case SCANICON:
// DW1 only
pp[0] = ScanIcon();
return 0;
case SCREENXPOS:
// Common to both DW1 & DW2
pp[0] = LToffset(SCREENXPOS);
return 0;
case SCREENYPOS:
// Common to both DW1 & DW2
pp[0] = LToffset(SCREENYPOS);
return 0;
case SCROLL:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 5; // 6 parameters
Scroll(coroParam, (EXTREME)pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pic->escOn, pic->myEscape);
return -6;
} else {
pp -= 3; // 4 parameters
Scroll(coroParam, EX_USEXY, pp[0], pp[1], pp[2], pp[2], pp[3], pic->escOn, pic->myEscape);
return -4;
}
case SCROLLPARAMETERS:
// DW2 only
pp -= 6; // 7 parameters
ScrollParameters(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
return -7;
case SENDTAG:
// DW2 only
pp -= 1; // 2 parameters
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
bool result;
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic, &result);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
pp[0] = result ? 1 : 0;
} else {
bool result;
SendTag(coroParam, pp[0], (TINSEL_EVENT)pp[1], pic->hPoly, pic->myEscape, &result);
if (coroParam)
return 0;
pp[0] = result;
}
return -1;
case SETACTOR:
// DW1 only
SetActor(pp[0]);
return -1;
case SETBLOCK:
// DW1 only
SetBlock(pp[0]);
return -1;
case SETEXIT:
// DW1 only
SetExit(pp[0]);
return -1;
case SETINVLIMIT:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
SetInvLimit(pp[0], pp[1]);
return -2;
case SETINVSIZE:
// Common to both DW1 & DW2
pp -= 6; // 7 parameters
SetInvSize(pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6]);
return -7;
case SETLANGUAGE:
// Common to both DW1 & DW2
SetLanguage((LANGUAGE)pp[0]);
return -1;
case SETPALETTE:
// Common to both DW1 & DW2
if (TinselV2) {
// Note: Although DW2 introduces parameters for start and length, it doesn't use them
pp -= 2;
SetPalette(pp[0], pic->escOn, pic->myEscape);
return -3;
} else {
SetPalette(pp[0], pic->escOn, pic->myEscape);
return -1;
}
case SETSYSTEMSTRING:
// DW2 only
pp -= 1; // 2 parameters
SetSystemString(pp[0], pp[1]);
return -2;
case SETSYSTEMVAR:
// DW1 only
pp -= 1; // 2 parameters
SetSystemVar(pp[0], pp[1]);
return -2;
case SETTAG:
// DW1 only
SetTag(coroParam, pp[0]);
return -1;
case SETTIMER:
// DW1 only
pp -= 3; // 4 parameters
SetTimer(pp[0], pp[1], pp[2], pp[3]);
return -4;
case SHELL:
// DW2 only
Shell(pp[0]);
return 0;
case SHOWACTOR:
// DW2 only
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
} else
ShowActorFn(coroParam, pp[0]);
return -1;
case SHOWBLOCK:
// DW2 only
ShowBlock(pp[0]);
return -1;
case SHOWEFFECT:
// DW2 only
ShowEffect(pp[0]);
return -1;
case SHOWMENU:
// DW2 only
ShowMenu();
return 0;
case SHOWPATH:
// DW2 only
ShowPath(pp[0]);
return -1;
case SHOWPOS:
// DW1 only
#ifdef DEBUG
showpos();
#endif
return 0;
case SHOWREFER:
// DW2 only
ShowRefer(pp[0]);
return -1;
case SHOWSTRING:
#ifdef DEBUG
showstring();
#endif
return 0;
case SHOWTAG:
// DW2 only
if (*pResumeState == RES_1 && pic->resumeCode == RES_WAITING) {
*pResumeState = RES_NOT;
FinishWaiting(coroParam, pic);
if (coroParam) {
*pResumeState = RES_1;
return 0;
}
} else {
ShowTag(coroParam, pp[0], pic->hPoly);
}
return -1;
case SPLAY:
// DW1 only
pp -= 6; // 7 parameters
if (pic->event == WALKIN || pic->event == WALKOUT)
SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], 0, pic->escOn, pic->myEscape);
else
SPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pp[6], pic->idActor, pic->escOn, pic->myEscape);
return -7;
case STAND:
// Common to both DW1 & DW2
pp -= 3; // 4 parameters
Stand(coroParam, pp[0], pp[1], pp[2], pp[3]);
return -4;
case STANDTAG:
// Common to both DW1 & DW2
StandTag(pp[0], pic->hPoly);
return -1;
case STARTGLOBALPROCESS:
// DW2 only
StartGlobalProcess(coroParam, pp[0]);
return -1;
case STARTPROCESS:
// DW2 only
StartProcess(coroParam, pp[0]);
return -1;
case STARTTIMER:
// DW2 only
pp -= 3; // 4 parameters
StartTimerFn(pp[0], pp[1], pp[2], pp[3]);
return -4;
case STOPMIDI:
// DW1 only
StopMidiFn();
return 0;
case STOPSAMPLE:
// Common to both DW1 & DW2
if (TinselV2) {
StopSample(pp[0]);
return -1;
} else {
StopSample();
return 0;
}
case STOPWALK:
// Common to both DW1 & DW2 only
StopWalk(pp[0]);
return -1;
case SUBTITLES:
// Common to both DW1 & DW2
Subtitles(pp[0]);
return -1;
case SWALK:
// Common to both DW1 & DW2
pp -= 5; // 6 parameters
Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], -1, pic->escOn, pic->myEscape);
return -6;
case SWALKZ:
// DW2 only
pp -= 6; // 7 parameters
Swalk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pp[5], pp[6], pic->escOn, pic->myEscape);
return -7;
case SYSTEMVAR:
// DW2 only
pp[0] = SystemVar(pp[0]);
return 0;
case TAGACTOR:
pp -= 2; // 3 parameters
TagActor(pp[0], pp[1], pp[2]);
return -3;
case TAGTAGXPOS:
case TAGTAGYPOS:
case TAGWALKXPOS:
case TAGWALKYPOS:
// DW2 only
pp[0] = TagPos((MASTER_LIB_CODES)libCode, pp[0], pic->hPoly);
return 0;
case TALK:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
if (TinselV2)
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
else if (pic->event == WALKIN || pic->event == WALKOUT)
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], 0, false, pic->escOn, pic->myEscape);
else
TalkOrSay(coroParam, IS_TALK, pp[1], 0, 0, pp[0], pic->idActor, false, pic->escOn, pic->myEscape);
return -2;
case TALKAT:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 4; // 5 parameters
TalkOrSay(coroParam, IS_TALKAT, pp[3], pp[1], pp[2], 0, pp[0], pp[4], pic->escOn, pic->myEscape);
return -5;
} else {
pp -= 3; // 4 parameters
TalkAt(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape);
return -4;
}
case TALKATS:
// DW1 only
pp -= 4; // 5 parameters
TalkAtS(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], pic->escOn, pic->myEscape);
return -5;
case TALKATTR:
// DW1 only
pp -= 2; // 3 parameters
TalkAttr(pp[0], pp[1], pp[2], pic->escOn, pic->myEscape);
return -3;
case TALKPALETTEINDEX:
// DW1 only
TalkPaletteIndex(pp[0]);
return -1;
case TALKRGB:
// DW2 only
TalkRGB(pp[0], pic->myEscape);
return -3;
case TALKVIA:
// DW2 only
TalkVia(pp[0]);
return -1;
case TEMPTAGFONT:
// DW2 only
TempTagFont(pp[0]);
return -1;
case TEMPTALKFONT:
// DW2 only
TempTalkFont(pp[0]);
return -1;
case THISOBJECT:
// DW2 only
pp[0] = ThisObject(pic->pinvo);
return 0;
case THISTAG:
// DW2 only
pp[0] = ThisTag(pic->hPoly);
return 0;
case TIMER:
// Common to both DW1 & DW2
pp[0] = TimerFn(pp[0]);
return 0;
case TOPIC:
// DW2 only
pp[0] = Topic();
return 0;
case TOPPLAY:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 3; // 4 parameters
TopPlay(coroParam, pp[0], pp[1], pp[2], pp[3], pic->myEscape, pic->event);
return -4;
} else {
pp -= 5; // 6 parameters
TopPlay(coroParam, pp[0], pp[1], pp[2], pp[5], pic->idActor, false, 0, pic->escOn, pic->myEscape);
return -6;
}
case TOPWINDOW:
// Common to both DW1 & DW2
TopWindow(pp[0]);
return -1;
case TRANSLUCENTINDEX:
// DW2 only
TranslucentIndex(pp[0]);
return -1;
case TRYPLAYSAMPLE:
// DW1 only
pp -= 1; // 2 parameters
TryPlaySample(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
case UNDIMMUSIC:
// DW2 only
UnDimMusic();
return 0;
case UNHOOKSCENE:
// Common to both DW1 & DW2
UnHookSceneFn();
return 0;
case UNTAGACTOR:
// DW1 only
UnTagActorFn(pp[0]);
return -1;
case VIBRATE:
// DW1 only
Vibrate();
return 0;
case WAITFRAME:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
WaitFrame(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
return -2;
case WAITKEY:
// Common to both DW1 & DW2
WaitKey(coroParam, pic->escOn, pic->myEscape);
return 0;
case WAITSCROLL:
// DW2 only
WaitScroll(coroParam, pic->myEscape);
return 0;
case WAITTIME:
// Common to both DW1 & DW2
pp -= 1; // 2 parameters
WaitTime(coroParam, pp[0], pp[1], pic->escOn, pic->myEscape);
if (!coroParam && (pic->hCode == 0x3007540) && (pic->resumeState == RES_2))
// FIXME: This is a hack to return control to the user after using the prunes in
// the DW1 Demo, since I can't figure out how it's legitimately done
Control(CONTROL_ON);
return -2;
case WALK:
// Common to both DW1 & DW2
pp -= 4; // 5 parameters
Walk(coroParam, pp[0], pp[1], pp[2], pp[3], pp[4], false, -1, pic->escOn, pic->myEscape);
return -5;
case WALKED: {
// Common to both DW1 & DW2
pp -= 3; // 4 parameters
bool tmp = false;
Walked(coroParam, pp[0], pp[1], pp[2], pp[3], pic->escOn, pic->myEscape, tmp);
if (!coroParam) {
// Only write the result to the stack if walked actually completed running.
pp[0] = tmp;
}
}
return -3;
case WALKINGACTOR:
// Common to both DW1 & DW2
if (TinselV2) {
// DW2 doesn't use a second parameter to WalkingActor
WalkingActor(pp[0]);
return -1;
} else {
pp -= 40; // 41 parameters
WalkingActor(pp[0], (SCNHANDLE *)&pp[1]);
return -41;
}
case WALKPOLY:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 1; // 2 parameters
WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
return -2;
} else {
pp -= 2; // 3 parameters
WalkPoly(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
return -3;
}
case WALKTAG:
// Common to both DW1 & DW2
if (TinselV2) {
pp -= 1; // 2 parameters
WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
return -2;
} else {
pp -= 2; // 3 parameters
WalkTag(coroParam, pp[0], pp[1], pic->hPoly, pic->escOn, pic->myEscape);
return -3;
}
case WALKXPOS:
// DW2 only
pp[0] = WalkXPos();
return 0;
case WALKYPOS:
// DW2 only
pp[0] = WalkYPos();
return 0;
case WHICHCD:
// DW2 only
pp[0] = WhichCd();
return 0;
case WHICHINVENTORY:
// Common to both DW1 & DW2
pp[0] = WhichInventory();
return 0;
case ZZZZZZ:
// DW2 only - dummy routine used during debugging
return -1;
default:
error("Unsupported library function");
}
//error("Can't possibly get here");
}
} // End of namespace Tinsel