Merged the tinsel 2 engine with tinsel 1. Both Discworld 1 and Discworld 2 should be completable

svn-id: r35196
This commit is contained in:
Filippos Karapetis 2008-12-01 20:35:36 +00:00
parent f10f151ff7
commit af945ac788
81 changed files with 17331 additions and 4740 deletions

File diff suppressed because it is too large Load diff

View file

@ -29,24 +29,30 @@
#include "tinsel/dw.h" // for SCNHANDLE #include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/events.h" // for USER_EVENT #include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/palette.h" // for COLORREF #include "tinsel/palette.h" // for COLORREF
namespace Tinsel { namespace Tinsel {
struct FREEL; struct FREEL;
struct INT_CONTEXT; struct INT_CONTEXT;
struct MACTOR; struct MOVER;
struct OBJECT; struct OBJECT;
#define ACTORTAG_KEY 0x1000000
#define OTH_RELATEDACTOR 0x00000fff
#define OTH_RELATIVE 0x00001000
#define OTH_ABSOLUTE 0x00002000
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
void RegisterActors(int num); void RegisterActors(int num);
void FreeActors(void); void FreeActors(void);
void setleadid(int rid); void SetLeadId(int rid);
int LeadId(void); int GetLeadId(void);
void StartActors(SCNHANDLE ah, int numActors, bool bRunScript); bool ActorIsGhost(int actor);
void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
void DropActors(void); // No actor reels running void DropActors(void); // No actor reels running
void DisableActor(int actor); void DisableActor(int actor);
void EnableActor(int actor); void EnableActor(int actor);
@ -63,60 +69,110 @@ int GetActorLeft(int ano);
int GetActorRight(int ano); int GetActorRight(int ano);
int GetActorTop(int ano); int GetActorTop(int ano);
int GetActorBottom(int ano); int GetActorBottom(int ano);
void HideActor(int ano); void ShowActor(CORO_PARAM, int ano);
void HideActor(CORO_PARAM, int ano);
bool ActorHidden(int ano);
bool HideMovingActor(int id, int sf); bool HideMovingActor(int id, int sf);
void unHideMovingActor(int id); void unHideMovingActor(int id);
void restoreMovement(int id); void restoreMovement(int id);
void storeActorReel(int ano, const FREEL *reel, SCNHANDLE film, OBJECT *pobj, int reelnum, int x, int y); void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y);
const FREEL *actorReel(int ano); const FREEL *actorReel(int ano);
SCNHANDLE actorFilm(int ano); SCNHANDLE actorFilm(int ano);
void setActorPlayFilm(int ano, SCNHANDLE film); void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE getActorPlayFilm(int ano); SCNHANDLE GetActorPlayFilm(int ano);
void setActorTalkFilm(int ano, SCNHANDLE film); void SetActorTalkFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE getActorTalkFilm(int ano); SCNHANDLE GetActorTalkFilm(int ano);
void setActorTalking(int ano, bool tf); void SetActorTalking(int ano, bool tf);
bool isActorTalking(int ano); bool ActorIsTalking(int ano);
void setActorLatestFilm(int ano, SCNHANDLE film); void SetActorLatestFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE getActorLatestFilm(int ano); SCNHANDLE GetActorLatestFilm(int ano);
void updateActorEsc(int ano, bool escOn, int escEv); void UpdateActorEsc(int ano, bool escOn, int escEvent);
bool actorEsc(int ano); void UpdateActorEsc(int ano, int escEvent);
int actorEev(int ano); bool ActorEsc(int ano);
void storeActorPos(int ano, int x, int y); int ActorEev(int ano);
void storeActorSteps(int ano, int steps); void StoreActorPos(int ano, int x, int y);
int getActorSteps(int ano); void StoreActorSteps(int ano, int steps);
void storeActorZpos(int ano, int z); int GetActorSteps(int ano);
void StoreActorZpos(int ano, int z, int column = -1);
int GetActorZpos(int ano, int column);
void IncLoopCount(int ano);
int GetLoopCount(int ano);
SCNHANDLE GetActorTag(int ano); SCNHANDLE GetActorTag(int ano);
void FirstTaggedActor(void); void FirstTaggedActor(void);
int NextTaggedActor(void); int NextTaggedActor(void);
int NextTaggedActor(int previous);
int AsetZPos(OBJECT *pObj, int y, int32 zFactor); int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
void MAsetZPos(MACTOR *pActor, int y, int32 zFactor); void SetMoverZ(MOVER *pMover, int y, int32 zFactor);
void actorEvent(int ano, USER_EVENT event, BUTEVENT be); void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
void storeActorAttr(int ano, int r1, int g1, int b1); void storeActorAttr(int ano, int r1, int g1, int b1);
COLORREF getActorTcol(int ano); COLORREF GetActorRGB(int ano);
void SetActorRGB(int ano, COLORREF colour);
void SetActorZfactor(int ano, uint32 zFactor);
uint32 GetActorZfactor(int ano);
void setactorson(void); void setactorson(void);
void ActorsLife(int id, bool bAlive); void ActorsLife(int id, bool bAlive);
void dwEndActor(int ano);
void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right);
SCNHANDLE GetActorTagHandle(int ano);
void SetActorPointedTo(int actor, bool bPointedTo);
bool ActorIsPointedTo(int actor);
void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
bool ActorTagIsWanted(int actor);
bool InHotSpot(int ano, int curX, int curY);
int FrontTaggedActor(void);
void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute);
bool IsTaggedActor(int actor);
void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y);
SCNHANDLE GetActorPresFilm(int ano);
int GetActorFilmNumber(int ano);
void StoreActorReel(int actor, int column, OBJECT *pObj);
void NotPlayingReel(int actor, int filmNumber, int column);
bool ActorReelPlaying(int actor, int column);
void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorPlayFilm(int ano);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
struct SAVED_ACTOR { struct SAVED_ACTOR {
short actorID; short actorID;
short z; short zFactor;
bool bAlive; bool bAlive;
bool bHidden;
SCNHANDLE presFilm; //!< the film that reel belongs to SCNHANDLE presFilm; //!< the film that reel belongs to
short presRnum; //!< the present reel number short presRnum; //!< the present reel number
short presX, presY; short presPlayX, presPlayY;
};
typedef SAVED_ACTOR *PSAVED_ACTOR;
#define NUM_ZPOSITIONS 200 // Reasonable-sounding number
struct Z_POSITIONS {
short actor;
short column;
int z;
}; };
int SaveActors(SAVED_ACTOR *sActorInfo); int SaveActors(SAVED_ACTOR *sActorInfo);
void RestoreActorProcess(int id, INT_CONTEXT *pic); void RestoreActorProcess(int id, INT_CONTEXT *pic);
int SaveActors(PSAVED_ACTOR sActorInfo);
void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo);
void SaveZpositions(void *zpp);
void RestoreZpositions(void *zpp);
void SaveActorZ(byte *saveActorZ);
void RestoreActorZ(byte *saveActorZ);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/

View file

@ -29,32 +29,12 @@
#include "tinsel/multiobj.h" // multi-part object defintions etc. #include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/tinsel.h"
#include "common/util.h" #include "common/util.h"
namespace Tinsel { namespace Tinsel {
/** Animation script commands */
enum {
ANI_END = 0, //!< end of animation script
ANI_JUMP = 1, //!< animation script jump
ANI_HFLIP = 2, //!< flip animated object horizontally
ANI_VFLIP = 3, //!< flip animated object vertically
ANI_HVFLIP = 4, //!< flip animated object in both directions
ANI_ADJUSTX = 5, //!< adjust animated object x animation point
ANI_ADJUSTY = 6, //!< adjust animated object y animation point
ANI_ADJUSTXY = 7, //!< adjust animated object x & y animation points
ANI_NOSLEEP = 8, //!< do not sleep for this frame
ANI_CALL = 9, //!< call routine
ANI_HIDE = 10 //!< hide animated object
};
/** animation script command possibilities */
union ANI_SCRIPT {
int32 op; //!< treat as an opcode or operand
uint32 hFrame; //!< treat as a animation frame handle
};
/** /**
* Advance to next frame routine. * Advance to next frame routine.
* @param pAnim Animation data structure * @param pAnim Animation data structure
@ -64,6 +44,9 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript); const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
while (1) { // repeat until a real image while (1) { // repeat until a real image
debugC(DEBUG_DETAILED, kTinselDebugAnimations,
"DoNextFrame %ph index=%d, op=%xh", (byte *)pAnim, pAnim->scriptIndex,
FROM_LE_32(pAni[pAnim->scriptIndex].op));
switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
case ANI_END: // end of animation script case ANI_END: // end of animation script
@ -104,7 +87,6 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
// go fetch a real image // go fetch a real image
break; break;
case ANI_HVFLIP: // flip animated object in both directions case ANI_HVFLIP: // flip animated object in both directions
// next opcode // next opcode
@ -230,6 +212,12 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) { void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
OBJECT *pObj; // multi-object list iterator OBJECT *pObj; // multi-object list iterator
debugC(DEBUG_DETAILED, kTinselDebugAnimations,
"InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
!pAniObj ? 0 : fracToInt(pAniObj->xPos),
!pAniObj ? 0 : fracToInt(pAniObj->yPos),
!pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (byte *)pAnim);
pAnim->aniDelta = 1; // will animate on next call to NextAnimRate pAnim->aniDelta = 1; // will animate on next call to NextAnimRate
pAnim->pObject = pAniObj; // set object to animate pAnim->pObject = pAniObj; // set object to animate
pAnim->hScript = hNewScript; // set animation script pAnim->hScript = hNewScript; // set animation script
@ -254,9 +242,13 @@ SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
// re-init animation delta counter // re-init animation delta counter
pAnim->aniDelta = pAnim->aniRate; pAnim->aniDelta = pAnim->aniRate;
if (TinselV2)
state = DoNextFrame(pAnim);
else {
// move to next frame // move to next frame
while ((state = DoNextFrame(pAnim)) == ScriptNoSleep) while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
; ;
}
return state; return state;
} }
@ -274,7 +266,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// get a pointer to the script // get a pointer to the script
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript); const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
if (numFrames <= 0) if (!TinselV2 && (numFrames <= 0))
// do nothing // do nothing
return; return;
@ -282,9 +274,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) { switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
case ANI_END: // end of animation script case ANI_END: // end of animation script
// going off the end is probably a error // going off the end is probably a error, but only in Tinsel 1
if (!TinselV2)
error("SkipFrames(): formally 'assert(0)!'"); error("SkipFrames(): formally 'assert(0)!'");
break; return;
case ANI_JUMP: // do animation jump case ANI_JUMP: // do animation jump
@ -293,6 +286,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// jump to new frame position // jump to new frame position
pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op); pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op);
if (TinselV2)
// Done if skip to jump
return;
break; break;
case ANI_HFLIP: // flip animated object horizontally case ANI_HFLIP: // flip animated object horizontally
@ -383,7 +380,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
default: // must be an actual animation frame handle default: // must be an actual animation frame handle
// one less frame // one less frame
if (numFrames-- > 0) { if (numFrames == 0)
return;
if (numFrames == -1 || numFrames-- > 0) {
// next opcode // next opcode
pAnim->scriptIndex++; pAnim->scriptIndex++;
} else { } else {
@ -401,4 +401,48 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
} }
} }
/**
* About to jump or end
* @param pAnim Animation data structure
*/
bool AboutToJumpOrEnd(PANIM pAnim) {
if (pAnim->aniDelta == 1) {
// get a pointer to the script
ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript);
int zzz = pAnim->scriptIndex;
for (;;) {
// repeat until a real image
switch (FROM_LE_32(pAni[zzz].op)) {
case ANI_END: // end of animation script
case ANI_JUMP: // do animation jump
return true;
case ANI_HFLIP: // flip animated object horizontally
case ANI_VFLIP: // flip animated object vertically
case ANI_HVFLIP: // flip animated object in both directions
zzz++;
break;
case ANI_ADJUSTX: // adjust animated object x animation point
case ANI_ADJUSTY: // adjust animated object y animation point
zzz += 2;
break;
case ANI_ADJUSTXY: // adjust animated object x & y animation points
zzz += 3;
break;
case ANI_HIDE: // hide animated object
default: // must be an actual animation frame handle
return false;
}
}
}
return false;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -41,6 +41,32 @@ struct ANIM {
uint32 hScript; //!< animation script handle uint32 hScript; //!< animation script handle
int scriptIndex; //!< current position in animation script int scriptIndex; //!< current position in animation script
}; };
typedef ANIM *PANIM;
typedef void (*PANI_ADDR)(struct ANIM *);
/** Animation script commands */
enum {
ANI_END = 0, //!< end of animation script
ANI_JUMP = 1, //!< animation script jump
ANI_HFLIP = 2, //!< flip animated object horizontally
ANI_VFLIP = 3, //!< flip animated object vertically
ANI_HVFLIP = 4, //!< flip animated object in both directions
ANI_ADJUSTX = 5, //!< adjust animated object x animation point
ANI_ADJUSTY = 6, //!< adjust animated object y animation point
ANI_ADJUSTXY = 7, //!< adjust animated object x & y animation points
ANI_NOSLEEP = 8, //!< do not sleep for this frame
ANI_CALL = 9, //!< call routine
ANI_HIDE = 10, //!< hide animated object
ANI_STOP = 11 //!< stop sound
};
/** animation script command possibilities */
union ANI_SCRIPT {
int32 op; //!< treat as an opcode or operand
uint32 hFrame; //!< treat as a animation frame handle
// PANI_ADDR pFunc; //!< treat as a animation function call
};
/*----------------------------------------------------------------------*\ /*----------------------------------------------------------------------*\
@ -66,6 +92,8 @@ void SkipFrames( // Skip the specified number of frames
ANIM *pAnim, // animation data structure ANIM *pAnim, // animation data structure
int numFrames); // number of frames to skip int numFrames); // number of frames to skip
bool AboutToJumpOrEnd(PANIM pAnim);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_ANIM_H #endif // TINSEL_ANIM_H

View file

@ -37,6 +37,9 @@ namespace Tinsel {
// current background // current background
BACKGND *pCurBgnd = NULL; BACKGND *pCurBgnd = NULL;
// FIXME: Not yet used
static bool bEntireRedraw;
/** /**
* Called to initialise a background. * Called to initialise a background.
* @param pBgnd Pointer to data struct for current background * @param pBgnd Pointer to data struct for current background
@ -124,6 +127,27 @@ void PlayfieldGetPos(int which, int *pXpos, int *pYpos) {
*pYpos = fracToInt(pPlayfield->fieldY); *pYpos = fracToInt(pPlayfield->fieldY);
} }
/**
* Returns the x position of the centre of the specified playfield
* @param which Which playfield
*/
int PlayfieldGetCentreX(int which) {
PLAYFIELD *pPlayfield; // pointer to relavent playfield
// make sure there is a background
assert(pCurBgnd != NULL);
// make sure the playfield number is in range
assert(which >= 0 && which < pCurBgnd->numPlayfields);
// get playfield pointer
pPlayfield = pCurBgnd->fieldArray + which;
// get current integer position
return fracToInt(pPlayfield->fieldX) + SCREEN_WIDTH/2;
}
/** /**
* Returns the display list for the specified playfield. * Returns the display list for the specified playfield.
* @param which Which playfield * @param which Which playfield
@ -229,4 +253,9 @@ void DrawBackgnd(void) {
ResetClipRect(); ResetClipRect();
} }
void ForceEntireRedraw(void) {
bEntireRedraw = true;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -27,10 +27,11 @@
#ifndef TINSEL_BACKGND_H // prevent multiple includes #ifndef TINSEL_BACKGND_H // prevent multiple includes
#define TINSEL_BACKGND_H #define TINSEL_BACKGND_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/palette.h" // palette definitions
#include "common/frac.h" #include "common/frac.h"
#include "common/rect.h" #include "common/rect.h"
#include "tinsel/coroutine.h"
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/palette.h" // palette definitions
namespace Tinsel { namespace Tinsel {
@ -78,6 +79,8 @@ struct BACKGND {
void InitBackground( // called to initialise a background void InitBackground( // called to initialise a background
BACKGND *pBgnd); // pointer to data struct for current background BACKGND *pBgnd); // pointer to data struct for current background
void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
void StopBgndScrolling(void); // Stops all background playfields from scrolling void StopBgndScrolling(void); // Stops all background playfields from scrolling
void PlayfieldSetPos( // Sets the xy position of the specified playfield in the current background void PlayfieldSetPos( // Sets the xy position of the specified playfield in the current background
@ -90,6 +93,9 @@ void PlayfieldGetPos( // Returns the xy position of the specified playfield in
int *pXpos, // returns current x position int *pXpos, // returns current x position
int *pYpos); // returns current y position int *pYpos); // returns current y position
int PlayfieldGetCentreX( // Returns the xy position of the specified playfield in the current background
int which); // which playfield
OBJECT *GetPlayfieldList( // Returns the display list for the specified playfield OBJECT *GetPlayfieldList( // Returns the display list for the specified playfield
int which); // which playfield int which); // which playfield
@ -100,7 +106,15 @@ void DrawBackgnd(void); // Draws all playfields for the current background
void RedrawBackgnd(void); // Completely redraws all the playfield object lists for the current background void RedrawBackgnd(void); // Completely redraws all the playfield object lists for the current background
SCNHANDLE BackPal(void); OBJECT *GetBgObject();
SCNHANDLE BgPal(void);
void ForceEntireRedraw(void);
int BgWidth(void);
int BgHeight(void);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -37,7 +37,8 @@
#include "tinsel/pid.h" #include "tinsel/pid.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/timers.h" // For ONE_SECOND constant #include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // For control() #include "tinsel/tinlib.h" // For Control()
#include "tinsel/tinsel.h"
#include "common/util.h" #include "common/util.h"
@ -45,49 +46,61 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
static SCNHANDLE BackPalette = 0; // Background's palette #define MAX_BG 10
static OBJECT *pBG = 0; // The main picture's object.
static SCNHANDLE hBgPal = 0; // Background's palette
static POBJECT pBG[MAX_BG];
static ANIM thisAnim[MAX_BG]; // used by BGmainProcess()
static int BGspeed = 0; static int BGspeed = 0;
static SCNHANDLE BgroundHandle = 0; // Current scene handle - stored in case of Save_Scene() static SCNHANDLE hBackground = 0; // Current scene handle - stored in case of Save_Scene()
static bool DoFadeIn = false; static bool bDoFadeIn = false;
static ANIM thisAnim; // used by BGmainProcess() static int bgReels;
/**
* GetBgObject
*/
OBJECT *GetBgObject() {
return pBG[0];
}
/** /**
* BackPal * BackPal
*/ */
SCNHANDLE BackPal(void) { SCNHANDLE BgPal(void) {
return BackPalette; return hBgPal;
} }
/** /**
* SetDoFadeIn * SetDoFadeIn
*/ */
void SetDoFadeIn(bool tf) { void SetDoFadeIn(bool tf) {
DoFadeIn = tf; bDoFadeIn = tf;
} }
/** /**
* Called before scene change. * Called before scene change.
*/ */
void DropBackground(void) { void DropBackground(void) {
pBG = NULL; // No background pBG[0] = NULL; // No background
BackPalette = 0; // No background palette
if (!TinselV2)
hBgPal = 0; // No background palette
} }
/** /**
* Return the width of the current background. * Return the width of the current background.
*/ */
int BackgroundWidth(void) { int BgWidth(void) {
assert(pBG); assert(pBG[0]);
return MultiRightmost(pBG) + 1; return MultiRightmost(pBG[0]) + 1;
} }
/** /**
* Return the height of the current background. * Return the height of the current background.
*/ */
int BackgroundHeight(void) { int BgHeight(void) {
assert(pBG); assert(pBG[0]);
return MultiLowest(pBG) + 1; return MultiLowest(pBG[0]) + 1;
} }
/** /**
@ -100,90 +113,137 @@ static void BGmainProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
const FREEL *pfr; const FILM *pFilm;
const FREEL *pReel;
const MULTI_INIT *pmi; const MULTI_INIT *pmi;
// get the stuff copied to process when it was created // get the stuff copied to process when it was created
pfr = (const FREEL *)param; if (pBG[0] == NULL) {
if (pBG == NULL) {
/*** At start of scene ***/ /*** At start of scene ***/
if (!TinselV2) {
pReel = (const FREEL *)param;
// Get the MULTI_INIT structure // Get the MULTI_INIT structure
pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfr->mobj)); pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj));
// Initialise and insert the object, and initialise its script. // Initialise and insert the object, and initialise its script.
pBG = MultiInitObject(pmi); pBG[0] = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG); MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[0]);
InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed); InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed);
bgReels = 1;
} else {
/*** At start of scene ***/
pFilm = (const FILM *)LockMem(hBackground);
bgReels = pFilm->numreels;
if (DoFadeIn) { int i;
FadeInFast(NULL); for (i = 0; i < bgReels; i++) {
DoFadeIn = false; // Get the MULTI_INIT structure
pmi = (PMULTI_INIT) LockMem(pFilm->reels[i].mobj);
// Initialise and insert the object, and initialise its script.
pBG[i] = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[i]);
MultiSetZPosition(pBG[i], 0);
InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed);
if (i > 0)
pBG[i-1]->pSlave = pBG[i];
}
} }
while (StepAnimScript(&thisAnim) != ScriptFinished) if (bDoFadeIn) {
CORO_SLEEP(1); FadeInFast(NULL);
bDoFadeIn = false;
} else if (TinselV2)
PokeInTagColour();
for (;;) {
for (int i = 0; i < bgReels; i++) {
if (StepAnimScript(&thisAnim[i]) == ScriptFinished)
error("Background animation has finished!"); error("Background animation has finished!");
}
CORO_SLEEP(1);
}
} else { } else {
// New background during scene // New background during scene
if (!TinselV2) {
pReel = (const FREEL *)param;
InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed);
StepAnimScript(&thisAnim[0]);
} else {
pFilm = (const FILM *)LockMem(hBackground);
assert(bgReels == pFilm->numreels);
// Just re-initialise the script. // Just re-initialise the scripts.
InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed); for (int i = 0; i < bgReels; i++) {
StepAnimScript(&thisAnim); InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed);
StepAnimScript(&thisAnim[i]);
}
}
} }
CORO_END_CODE; CORO_END_CODE;
} }
/** /**
* setBackPal() * AetBgPal()
*/ */
void setBackPal(SCNHANDLE hPal) { void SetBackPal(SCNHANDLE hPal) {
BackPalette = hPal; hBgPal = hPal;
fettleFontPal(BackPalette); FettleFontPal(hBgPal);
CreateTranslucentPalette(BackPalette); CreateTranslucentPalette(hBgPal);
} }
void ChangePalette(SCNHANDLE hPal) { void ChangePalette(SCNHANDLE hPal) {
SwapPalette(FindPalette(BackPalette), hPal); SwapPalette(FindPalette(hBgPal), hPal);
setBackPal(hPal); SetBackPal(hPal);
} }
/** /**
* Given the scene background film, extracts the palette handle for * Given the scene background film, extracts the palette handle for
* everything else's use, then starts a display process for each reel * everything else's use, then starts a display process for each reel
* in the film. * in the film.
* @param bfilm Scene background film * @param hFilm Scene background film
*/ */
void startupBackground(SCNHANDLE bfilm) { void StartupBackground(CORO_PARAM, SCNHANDLE hFilm) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
const FILM *pfilm; const FILM *pfilm;
IMAGE *pim; IMAGE *pim;
BgroundHandle = bfilm; // Save handle in case of Save_Scene() hBackground = hFilm; // Save handle in case of Save_Scene()
pim = GetImageFromFilm(bfilm, 0, NULL, NULL, &pfilm); pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm);
setBackPal(FROM_LE_32(pim->hImgPal)); SetBackPal(FROM_LE_32(pim->hImgPal));
// Extract the film speed // Extract the film speed
BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate); BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate);
if (pBG == NULL)
control(CONTROL_STARTOFF); // New feature - start scene with control off
// Start display process for each reel in the film // Start display process for each reel in the film
assert(FROM_LE_32(pfilm->numreels) == 1); // Multi-reeled backgrounds withdrawn
g_scheduler->createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL)); g_scheduler->createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL));
if (pBG[0] == NULL)
ControlStartOff();
if (TinselV2 && (coroParam != nullContext))
CORO_GIVE_WAY;
CORO_END_CODE;
} }
/** /**
* Return the current scene handle. * Return the current scene handle.
*/ */
SCNHANDLE GetBgroundHandle(void) { SCNHANDLE GetBgroundHandle(void) {
return BgroundHandle; return hBackground;
} }
} // end of namespace Tinsel } // end of namespace Tinsel

1272
engines/tinsel/bmv.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,8 @@
* This file contains configuration functionality * This file contains configuration functionality
*/ */
//#define USE_3FLAGS 1
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"
@ -39,7 +41,7 @@ namespace Tinsel {
//----------------- GLOBAL GLOBAL DATA -------------------- //----------------- GLOBAL GLOBAL DATA --------------------
int dclickSpeed = DOUBLE_CLICK_TIME; int dclickSpeed = DOUBLE_CLICK_TIME;
int volMidi = Audio::Mixer::kMaxChannelVolume; int volMusic = Audio::Mixer::kMaxChannelVolume;
int volSound = Audio::Mixer::kMaxChannelVolume; int volSound = Audio::Mixer::kMaxChannelVolume;
int volVoice = Audio::Mixer::kMaxChannelVolume; int volVoice = Audio::Mixer::kMaxChannelVolume;
int speedText = DEFTEXTSPEED; int speedText = DEFTEXTSPEED;
@ -49,21 +51,18 @@ LANGUAGE g_language = TXT_ENGLISH;
int bAmerica = 0; int bAmerica = 0;
// Shouldn't really be here, but time is short...
bool bNoBlocking;
/** /**
* Write settings to config manager and flush the config file to disk. * Write settings to config manager and flush the config file to disk.
*/ */
void WriteConfig(void) { void WriteConfig(void) {
ConfMan.setInt("dclick_speed", dclickSpeed); ConfMan.setInt("dclick_speed", dclickSpeed);
ConfMan.setInt("music_volume", volMidi); ConfMan.setInt("music_volume", volMusic);
ConfMan.setInt("sfx_volume", volSound); ConfMan.setInt("sfx_volume", volSound);
ConfMan.setInt("speech_volume", volVoice); ConfMan.setInt("speech_volume", volVoice);
ConfMan.setInt("talkspeed", (speedText * 255) / 100); ConfMan.setInt("talkspeed", (speedText * 255) / 100);
ConfMan.setBool("subtitles", bSubtitles); ConfMan.setBool("subtitles", bSubtitles);
//ConfMan.setBool("swap_buttons", bSwapButtons ? 1 : 0); //ConfMan.setBool("swap_buttons", bSwapButtons ? 1 : 0);
//ConfigData.bAmerica = bAmerica; // EN_USA / EN_GRB
// Store language for multilingual versions // Store language for multilingual versions
if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) { if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
@ -92,16 +91,14 @@ void WriteConfig(void) {
ConfMan.flushToDisk(); ConfMan.flushToDisk();
} }
/*---------------------------------------------------------------------*\ /**
| ReadConfig() | * Read configuration settings from the config file into memory
|-----------------------------------------------------------------------| */
|
\*---------------------------------------------------------------------*/
void ReadConfig(void) { void ReadConfig(void) {
if (ConfMan.hasKey("dclick_speed")) if (ConfMan.hasKey("dclick_speed"))
dclickSpeed = ConfMan.getInt("dclick_speed"); dclickSpeed = ConfMan.getInt("dclick_speed");
volMidi = ConfMan.getInt("music_volume"); volMusic = ConfMan.getInt("music_volume");
volSound = ConfMan.getInt("sfx_volume"); volSound = ConfMan.getInt("sfx_volume");
volVoice = ConfMan.getInt("speech_volume"); volVoice = ConfMan.getInt("speech_volume");

View file

@ -37,7 +37,7 @@ enum {
}; };
extern int dclickSpeed; extern int dclickSpeed;
extern int volMidi; extern int volMusic;
extern int volSound; extern int volSound;
extern int volVoice; extern int volVoice;
extern int speedText; extern int speedText;
@ -51,10 +51,6 @@ void ReadConfig(void);
extern bool isJapanMode(); extern bool isJapanMode();
// Shouldn't really be here, but time is short...
extern bool bNoBlocking;
} // end of namespace Tinsel } // end of namespace Tinsel
#endif #endif

View file

@ -70,6 +70,7 @@ struct CoroBaseContext {
typedef CoroBaseContext *CoroContext; typedef CoroBaseContext *CoroContext;
extern CoroContext nullContext;
/** /**
* Wrapper class which holds a pointer to a pointer to a CoroBaseContext. * Wrapper class which holds a pointer to a pointer to a CoroBaseContext.
@ -101,6 +102,7 @@ public:
#define CORO_END_CONTEXT(x) } *x = (CoroContextTag *)coroParam #define CORO_END_CONTEXT(x) } *x = (CoroContextTag *)coroParam
#define CORO_BEGIN_CODE(x) \ #define CORO_BEGIN_CODE(x) \
if (&coroParam == &nullContext) assert(!nullContext);\
if (!x) {coroParam = x = new CoroContextTag();}\ if (!x) {coroParam = x = new CoroContextTag();}\
assert(coroParam);\ assert(coroParam);\
assert(coroParam->_sleep >= 0);\ assert(coroParam->_sleep >= 0);\
@ -109,17 +111,22 @@ public:
switch(coroParam->_line) { case 0:; switch(coroParam->_line) { case 0:;
#define CORO_END_CODE \ #define CORO_END_CODE \
if (&coroParam == &nullContext) nullContext = NULL; \
} }
#define CORO_SLEEP(delay) \ #define CORO_SLEEP(delay) do {\
do {\
coroParam->_line = __LINE__;\ coroParam->_line = __LINE__;\
coroParam->_sleep = delay;\ coroParam->_sleep = delay;\
assert(&coroParam != &nullContext);\
return; case __LINE__:;\ return; case __LINE__:;\
} while (0) } while (0)
#define CORO_GIVE_WAY do { g_scheduler->giveWay(); CORO_SLEEP(1); } while (0)
#define CORO_RESCHEDULE do { g_scheduler->reschedule(); CORO_SLEEP(1); } while (0)
/** Stop the currently running coroutine */ /** Stop the currently running coroutine */
#define CORO_KILL_SELF() do { coroParam->_sleep = -1; return; } while(0) #define CORO_KILL_SELF() \
do { if (&coroParam != &nullContext) { coroParam->_sleep = -1; } return; } while(0)
/** Invoke another coroutine */ /** Invoke another coroutine */
#define CORO_INVOKE_ARGS(subCoro, ARGS) \ #define CORO_INVOKE_ARGS(subCoro, ARGS) \
@ -130,9 +137,22 @@ public:
subCoro ARGS;\ subCoro ARGS;\
if (!coroParam->_subctx) break;\ if (!coroParam->_subctx) break;\
coroParam->_sleep = coroParam->_subctx->_sleep;\ coroParam->_sleep = coroParam->_subctx->_sleep;\
assert(&coroParam != &nullContext);\
return; case __LINE__:;\ return; case __LINE__:;\
} while(1);\ } while(1);\
} while (0) } while (0)
#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS) \
do {\
coroParam->_line = __LINE__;\
coroParam->_subctx = 0;\
do {\
subCoro ARGS;\
if (!coroParam->_subctx) break;\
coroParam->_sleep = coroParam->_subctx->_sleep;\
assert(&coroParam != &nullContext);\
return RESULT; case __LINE__:;\
} while(1);\
} while (0)
#define CORO_INVOKE_0(subCoroutine) \ #define CORO_INVOKE_0(subCoroutine) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX)) CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX))
@ -140,6 +160,8 @@ public:
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0)) CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0))
#define CORO_INVOKE_2(subCoroutine, a0,a1) \ #define CORO_INVOKE_2(subCoroutine, a0,a1) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1)) CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1))
#define CORO_INVOKE_3(subCoroutine, a0,a1,a2) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1,a2))
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -34,11 +34,14 @@
#include "tinsel/film.h" #include "tinsel/film.h"
#include "tinsel/graphics.h" #include "tinsel/graphics.h"
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/inventory.h" #include "tinsel/dialogs.h"
#include "tinsel/multiobj.h" // multi-part object defintions etc. #include "tinsel/multiobj.h" // multi-part object defintions etc.
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/pid.h" #include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/timers.h" // For ONE_SECOND constant #include "tinsel/timers.h" // For ONE_SECOND constant
#include "tinsel/tinlib.h" // resetidletime() #include "tinsel/tinlib.h" // resetidletime()
#include "tinsel/tinsel.h" // For engine access #include "tinsel/tinsel.h" // For engine access
@ -54,20 +57,21 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
static OBJECT *McurObj = 0; // Main cursor object static OBJECT *McurObj = NULL; // Main cursor object
static OBJECT *AcurObj = 0; // Auxiliary cursor object static OBJECT *AcurObj = NULL; // Auxiliary cursor object
static ANIM McurAnim = {0,0,0,0,0}; // Main cursor animation structure static ANIM McurAnim = {0,0,0,0,0}; // Main cursor animation structure
static ANIM AcurAnim = {0,0,0,0,0}; // Auxiliary cursor animation structure static ANIM AcurAnim = {0,0,0,0,0}; // Auxiliary cursor animation structure
static bool bHiddenCursor = false; // Set when cursor is hidden static bool bHiddenCursor = false; // Set when cursor is hidden
static bool bTempNoTrailers = false; // Set when cursor trails are hidden static bool bTempNoTrailers = false; // Set when cursor trails are hidden
static bool bTempHide = false; // Set when cursor is hidden
static bool bFrozenCursor = false; // Set when cursor position is frozen static bool bFrozenCursor = false; // Set when cursor position is frozen
static frac_t IterationSize = 0; static frac_t IterationSize = 0;
static SCNHANDLE CursorHandle = 0; // Handle to cursor reel data static SCNHANDLE hCursorFilm = 0; // Handle to cursor reel data
static int numTrails = 0; static int numTrails = 0;
static int nextTrail = 0; static int nextTrail = 0;
@ -76,8 +80,11 @@ static bool bWhoa = false; // Set by DropCursor() at the end of a scene
// - causes cursor processes to do nothing // - causes cursor processes to do nothing
// Reset when main cursor has re-initialised // Reset when main cursor has re-initialised
static bool restart = false; // When main cursor has been bWhoa-ed, it waits static uint16 restart = 0; // When main cursor has been bWhoa-ed, it waits
// for this to be set to true. // for this to be set to 0x8000.
// Main cursor sets all the bits after a re-start
// - each cursor trail examines it's own bit
// to trigger a trail restart.
static short ACoX = 0, ACoY = 0; // Auxillary cursor image's animation offsets static short ACoX = 0, ACoY = 0; // Auxillary cursor image's animation offsets
@ -97,7 +104,7 @@ static int lastCursorX = 0, lastCursorY = 0;
//----------------- FORWARD REFERENCES -------------------- //----------------- FORWARD REFERENCES --------------------
static void MoveCursor(void); static void DoCursorMove(void);
/** /**
* Initialise and insert a cursor trail object, set its Z-pos, and hide * Initialise and insert a cursor trail object, set its Z-pos, and hide
@ -117,9 +124,9 @@ static void InitCurTrailObj(int i, int x, int y) {
if (ntrailData[i].trailObj != NULL) if (ntrailData[i].trailObj != NULL)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), ntrailData[i].trailObj); MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), ntrailData[i].trailObj);
pim = GetImageFromFilm(CursorHandle, i+1, &pfr, &pmi, &pfilm);// Get pointer to image pim = GetImageFromFilm(hCursorFilm, i+1, &pfr, &pmi, &pfilm);// Get pointer to image
assert(BackPal()); // No background palette assert(BgPal()); // No background palette
pim->hImgPal = TO_LE_32(BackPal()); pim->hImgPal = TO_LE_32(BgPal());
// Initialise and insert the object, set its Z-pos, and hide it // Initialise and insert the object, set its Z-pos, and hide it
ntrailData[i].trailObj = MultiInitObject(pmi); ntrailData[i].trailObj = MultiInitObject(pmi);
@ -140,8 +147,8 @@ static bool GetDriverPosition(int *x, int *y) {
*x = ptMouse.x; *x = ptMouse.x;
*y = ptMouse.y; *y = ptMouse.y;
return(*x >= 0 && *x <= SCREEN_WIDTH-1 && return(*x >= 0 && *x <= SCREEN_WIDTH - 1 &&
*y >= 0 && *y <= SCREEN_HEIGHT-1); *y >= 0 && *y <= SCREEN_HEIGHT - 1);
} }
/** /**
@ -154,7 +161,7 @@ void AdjustCursorXY(int deltaX, int deltaY) {
if (GetDriverPosition(&x, &y)) if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(x + deltaX, y + deltaY)); _vm->setMousePosition(Common::Point(x + deltaX, y + deltaY));
} }
MoveCursor(); DoCursorMove();
} }
/** /**
@ -170,7 +177,7 @@ void SetCursorXY(int newx, int newy) {
if (GetDriverPosition(&x, &y)) if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy)); _vm->setMousePosition(Common::Point(newx, newy));
MoveCursor(); DoCursorMove();
} }
/** /**
@ -181,7 +188,7 @@ void SetCursorScreenXY(int newx, int newy) {
if (GetDriverPosition(&x, &y)) if (GetDriverPosition(&x, &y))
_vm->setMousePosition(Common::Point(newx, newy)); _vm->setMousePosition(Common::Point(newx, newy));
MoveCursor(); DoCursorMove();
} }
/** /**
@ -229,7 +236,7 @@ void RestoreMainCursor(void) {
const FILM *pfilm; const FILM *pfilm;
if (McurObj != NULL) { if (McurObj != NULL) {
pfilm = (const FILM *)LockMem(CursorHandle); pfilm = (const FILM *)LockMem(hCursorFilm);
InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfilm->reels->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfilm->reels->script), ONE_SECOND / FROM_LE_32(pfilm->frate));
StepAnimScript(&McurAnim); StepAnimScript(&McurAnim);
@ -281,6 +288,13 @@ void FreezeCursor(void) {
bFrozenCursor = true; bFrozenCursor = true;
} }
/**
* Freeze the cursor, or not.
*/
void DoFreezeCursor(bool bFreeze) {
bFrozenCursor = bFreeze;
}
/** /**
* HideCursorTrails * HideCursorTrails
*/ */
@ -365,11 +379,12 @@ void SetAuxCursor(SCNHANDLE hFilm) {
GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear GetCursorXY(&x, &y, false); // Note: also waits for cursor to appear
pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image pim = GetImageFromFilm(hFilm, 0, &pfr, &pmi, &pfilm);// Get pointer to image
assert(BackPal()); // no background palette assert(BgPal()); // no background palette
pim->hImgPal = TO_LE_32(BackPal()); // Poke in the background palette pim->hImgPal = TO_LE_32(BgPal()); // Poke in the background palette
ACoX = (short)(FROM_LE_16(pim->imgWidth)/2 - ((int16) FROM_LE_16(pim->anioffX))); ACoX = (short)(FROM_LE_16(pim->imgWidth)/2 - ((int16) FROM_LE_16(pim->anioffX)));
ACoY = (short)(FROM_LE_16(pim->imgHeight)/2 - ((int16) FROM_LE_16(pim->anioffY))); ACoY = (short)((FROM_LE_16(pim->imgHeight) & ~C16_FLAG_MASK)/2 -
((int16) FROM_LE_16(pim->anioffY)));
// Initialise and insert the auxillary cursor object // Initialise and insert the auxillary cursor object
AcurObj = MultiInitObject(pmi); AcurObj = MultiInitObject(pmi);
@ -387,7 +402,7 @@ void SetAuxCursor(SCNHANDLE hFilm) {
/** /**
* MoveCursor * MoveCursor
*/ */
static void MoveCursor(void) { static void DoCursorMove(void) {
int startX, startY; int startX, startY;
Common::Point ptMouse; Common::Point ptMouse;
frac_t newX, newY; frac_t newX, newY;
@ -459,22 +474,30 @@ static void MoveCursor(void) {
* Initialise cursor object. * Initialise cursor object.
*/ */
static void InitCurObj(void) { static void InitCurObj(void) {
const FILM *pfilm; const FILM *pFilm;
const FREEL *pfr; const FREEL *pfr;
const MULTI_INIT *pmi; const MULTI_INIT *pmi;
IMAGE *pim; IMAGE *pim;
pim = GetImageFromFilm(CursorHandle, 0, &pfr, &pmi, &pfilm);// Get pointer to image if (TinselV2) {
assert(BackPal()); // no background palette pFilm = (const FILM *)LockMem(hCursorFilm);
pim->hImgPal = TO_LE_32(BackPal()); pfr = (const FREEL *)&pFilm->reels[0];
//--- pmi = (MULTI_INIT *)LockMem(pfr->mobj);
PokeInPalette(pmi);
} else {
assert(BgPal()); // no background palette
pim = GetImageFromFilm(hCursorFilm, 0, &pfr, &pmi, &pFilm);// Get pointer to image
pim->hImgPal = TO_LE_32(BgPal());
AcurObj = NULL; // No auxillary cursor AcurObj = NULL; // No auxillary cursor
}
McurObj = MultiInitObject(pmi); McurObj = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_STATUS), McurObj); MultiInsertObject(GetPlayfieldList(FIELD_STATUS), McurObj);
InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pfilm->frate)); InitStepAnimScript(&McurAnim, McurObj, FROM_LE_32(pfr->script), ONE_SECOND / FROM_LE_32(pFilm->frate));
} }
/** /**
@ -486,7 +509,7 @@ static void InitCurPos(void) {
lastCursorY = ptMouse.y; lastCursorY = ptMouse.y;
MultiSetZPosition(McurObj, Z_CURSOR); MultiSetZPosition(McurObj, Z_CURSOR);
MoveCursor(); DoCursorMove();
MultiHideObject(McurObj); MultiHideObject(McurObj);
IterationSize = ITERATION_BASE; IterationSize = ITERATION_BASE;
@ -505,16 +528,16 @@ static void CursorStoppedCheck(CORO_PARAM) {
// If scene is closing down // If scene is closing down
if (bWhoa) { if (bWhoa) {
// ...wait for next scene start-up // ...wait for next scene start-up
while (!restart) while (restart != 0x8000)
CORO_SLEEP(1); CORO_SLEEP(1);
// Re-initialise // Re-initialise
InitCurObj(); InitCurObj();
InitCurPos(); InitCurPos();
InventoryIconCursor(); // May be holding something InventoryIconCursor(false); // May be holding something
// Re-start the cursor trails // Re-start the cursor trails
restart = false; // set all bits restart = (uint16)-1; // set all bits
bWhoa = false; bWhoa = false;
} }
CORO_END_CODE; CORO_END_CODE;
@ -530,15 +553,15 @@ void CursorProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
while (!CursorHandle || !BackPal()) while (!hCursorFilm || !BgPal())
CORO_SLEEP(1); CORO_SLEEP(1);
InitCurObj(); InitCurObj();
InitCurPos(); InitCurPos();
InventoryIconCursor(); // May be holding something InventoryIconCursor(false); // May be holding something
bWhoa = false; bWhoa = false;
restart = false; restart = 0;
while (1) { while (1) {
// allow rescheduling // allow rescheduling
@ -562,10 +585,10 @@ void CursorProcess(CORO_PARAM, const void *) {
// Move the cursor as appropriate // Move the cursor as appropriate
if (!bFrozenCursor) if (!bFrozenCursor)
MoveCursor(); DoCursorMove();
// If the cursor should be hidden... // If the cursor should be hidden...
if (bHiddenCursor) { if (bHiddenCursor || bTempHide) {
// ...hide the cursor object(s) // ...hide the cursor object(s)
MultiHideObject(McurObj); MultiHideObject(McurObj);
if (AcurObj) if (AcurObj)
@ -595,9 +618,9 @@ void CursorProcess(CORO_PARAM, const void *) {
void DwInitCursor(SCNHANDLE bfilm) { void DwInitCursor(SCNHANDLE bfilm) {
const FILM *pfilm; const FILM *pfilm;
CursorHandle = bfilm; hCursorFilm = bfilm;
pfilm = (const FILM *)LockMem(CursorHandle); pfilm = (const FILM *)LockMem(hCursorFilm);
numTrails = FROM_LE_32(pfilm->numreels) - 1; numTrails = FROM_LE_32(pfilm->numreels) - 1;
assert(numTrails <= MAX_TRAILERS); assert(numTrails <= MAX_TRAILERS);
@ -607,6 +630,15 @@ void DwInitCursor(SCNHANDLE bfilm) {
* DropCursor is called when a scene is closing down. * DropCursor is called when a scene is closing down.
*/ */
void DropCursor(void) { void DropCursor(void) {
if (TinselV2) {
if (AcurObj)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), AcurObj);
if (McurObj)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), McurObj);
restart = 0;
}
AcurObj = NULL; // No auxillary cursor AcurObj = NULL; // No auxillary cursor
McurObj = NULL; // No cursor object (imminently deleted elsewhere) McurObj = NULL; // No cursor object (imminently deleted elsewhere)
bHiddenCursor = false; // Not hidden in next scene bHiddenCursor = false; // Not hidden in next scene
@ -625,7 +657,7 @@ void DropCursor(void) {
* RestartCursor is called when a new scene is starting up. * RestartCursor is called when a new scene is starting up.
*/ */
void RestartCursor(void) { void RestartCursor(void) {
restart = true; // Get the main cursor to re-initialise restart = 0x8000; // Get the main cursor to re-initialise
} }
/** /**
@ -639,10 +671,22 @@ void RebootCursor(void) {
bHiddenCursor = bTempNoTrailers = bFrozenCursor = false; bHiddenCursor = bTempNoTrailers = bFrozenCursor = false;
CursorHandle = 0; hCursorFilm = 0;
bWhoa = false; bWhoa = false;
restart = false; restart = 0;
}
void StartCursorFollowed(void) {
DelAuxCursor();
if (!SysVar(SV_ENABLEPRINTCURSOR))
bTempHide = true;
}
void EndCursorFollowed(void) {
InventoryIconCursor(false); // May be holding something
bTempHide = false;
} }
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -42,6 +42,7 @@ void SetTempCursor(SCNHANDLE pScript);
void DwHideCursor(void); void DwHideCursor(void);
void UnHideCursor(void); void UnHideCursor(void);
void FreezeCursor(void); void FreezeCursor(void);
void DoFreezeCursor(bool bFreeze);
void HideCursorTrails(void); void HideCursorTrails(void);
void UnHideCursorTrails(void); void UnHideCursorTrails(void);
void DelAuxCursor(void); void DelAuxCursor(void);
@ -50,6 +51,8 @@ void DwInitCursor(SCNHANDLE bfilm);
void DropCursor(void); void DropCursor(void);
void RestartCursor(void); void RestartCursor(void);
void RebootCursor(void); void RebootCursor(void);
void StartCursorFollowed(void);
void EndCursorFollowed(void);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -25,7 +25,7 @@
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
#include "tinsel/debugger.h" #include "tinsel/debugger.h"
#include "tinsel/inventory.h" #include "tinsel/dialogs.h"
#include "tinsel/pcode.h" #include "tinsel/pcode.h"
#include "tinsel/scene.h" #include "tinsel/scene.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"

View file

@ -31,7 +31,6 @@
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
#include "tinsel/savescn.h" // needed by TinselMetaEngine::listSaves #include "tinsel/savescn.h" // needed by TinselMetaEngine::listSaves
namespace Tinsel { namespace Tinsel {
struct TinselGameDescription { struct TinselGameDescription {
@ -68,7 +67,7 @@ uint16 TinselEngine::getVersion() const {
static const PlainGameDescriptor tinselGames[] = { static const PlainGameDescriptor tinselGames[] = {
{"tinsel", "Tinsel engine game"}, {"tinsel", "Tinsel engine game"},
{"dw", "Discworld"}, {"dw", "Discworld"},
{"dw2", "Discworld 2: Mortality Bytes!"}, {"dw2", "Discworld 2: Missing Presumed ...!?"},
{0, 0} {0, 0}
}; };
@ -77,25 +76,15 @@ namespace Tinsel {
static const TinselGameDescription gameDescriptions[] = { static const TinselGameDescription gameDescriptions[] = {
// The DW1 demo was based on an older revision of the Tinsel engine // Note: The following is the (hopefully) definitive list of version details:
// than the one used in the released game. We call it Tinsel v0 as // TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
// opposed to v1 which was used in the full retail version of DW. // of the Tinsel engine and graphics compression, and isn't currently supported
// TINSEL_V1: There were two versions of the Discworld 1 game - the first used .GRA
{ // Demo from http://www.adventure-treff.de/specials/dl_demos.php // files, and the second used .SCN files. The second also provided some fixes to
{ // various script bugs and coding errors, but is still considered TINSEL_V1,
"dw", // as both game versions work equally well with the newer code.
"Demo", // TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192), // and as far as we know there aren't any variations of this engine.
//AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GID_DW1,
0,
GF_DEMO,
TINSEL_V0,
},
{ {
{ // This version has *.gra files { // This version has *.gra files
@ -112,49 +101,6 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1, TINSEL_V1,
}, },
{ // Multilingual floppy with *.gra files.
// Note: It contains no english subtitles.
// Reported on our forums.
{
"dw",
"Floppy",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"french.txt", 0, NULL, -1},
{"german.txt", 0, NULL, -1},
{"italian.txt", 0, NULL, -1},
{"spanish.txt", 0, NULL, -1},
{NULL, 0, NULL, 0}
},
Common::FR_FRA,
Common::kPlatformPC,
Common::ADGF_DROPLANGUAGE
},
GID_DW1,
0,
GF_FLOPPY | GF_USE_4FLAGS,
TINSEL_V1,
},
{ // English CD. This version has *.gra files
{
"dw",
"CD",
{
{"dw.gra", 0, "c8808ccd988d603dd35dff42013ae7fd", 781656},
{"english.smp", 0, NULL, -1},
{NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GID_DW1,
0,
GF_CD,
TINSEL_V1,
},
{ // Multilingual CD with english speech and *.gra files. { // Multilingual CD with english speech and *.gra files.
// Note: It contains no english subtitles. // Note: It contains no english subtitles.
{ {
@ -245,7 +191,7 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1, TINSEL_V1,
}, },
{ // English CD with SCN files { // English CD v2
{ {
"dw", "dw",
"CD", "CD",
@ -265,7 +211,7 @@ static const TinselGameDescription gameDescriptions[] = {
}, },
#if 0 #if 0
{ // English Saturn CD. Not (yet?) supported { // English Saturn CD
{ {
"dw", "dw",
"CD", "CD",
@ -285,6 +231,25 @@ static const TinselGameDescription gameDescriptions[] = {
}, },
#endif #endif
// Currently disabled since it isn't really supported
#if 0
{ // Demo from http://www.adventure-treff.de/specials/dl_demos.php
{
"dw",
"Demo",
AD_ENTRY1s("dw.gra", "ce1b57761ba705221bcf70955b827b97", 441192),
//AD_ENTRY1s("dw.scn", "ccd72f02183d0e96b6e7d8df9492cda8", 23308),
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_DEMO
},
GID_DW1,
0,
GF_DEMO,
TINSEL_V0,
},
#endif
{ // German CD re-release "Neon Edition" { // German CD re-release "Neon Edition"
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT // Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
{ {
@ -301,6 +266,44 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1, TINSEL_V1,
}, },
{ // Europen/Australian Discworld 2 release
{
"dw2",
"CD",
{
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
{"english1.smp", 0, NULL, -1},
{NULL, 0, NULL, 0}
},
Common::EN_ANY,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GID_DW2,
0,
GF_CD | GF_SCNFILES,
TINSEL_V2,
},
{ // German Discworld 2 re-release "Neon Edition"
{
"dw2",
"CD",
{
{"dw2.scn", 0, "c6d15ce9720a9d8fef06e6582dcf3f34", 103593},
{"german1.smp", 0, NULL, -1},
{NULL, 0, NULL, 0}
},
Common::DE_DEU,
Common::kPlatformPC,
Common::ADGF_NO_FLAGS
},
GID_DW2,
0,
GF_CD | GF_SCNFILES,
TINSEL_V2,
},
{ AD_TABLE_END_MARKER, 0, 0, 0, 0 } { AD_TABLE_END_MARKER, 0, 0, 0, 0 }
}; };
@ -334,25 +337,23 @@ public:
} }
virtual const char *getCopyright() const { virtual const char *getCopyright() const {
// FIXME: Bad copyright string. return "Tinsel (C) Psygnosis";
// Should be something like "Tinsel (C) Psygnosis" or so... ???
return "Tinsel Engine";
} }
virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const; virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const; virtual bool hasFeature(MetaEngineFeature f) const;
virtual SaveStateList listSaves(const char *target) const; virtual SaveStateList listSaves(const char *target) const;
virtual int getMaximumSaveSlot() const;
}; };
bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const { bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
return return (f == kSupportsListSaves);
(f == kSupportsListSaves);
} }
namespace Tinsel { namespace Tinsel {
extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target); extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target);
} }
SaveStateList TinselMetaEngine::listSaves(const char *target) const { SaveStateList TinselMetaEngine::listSaves(const char *target) const {
@ -368,8 +369,6 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
return saveList; return saveList;
} }
int TinselMetaEngine::getMaximumSaveSlot() const { return 999; }
bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const { bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const {
const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc; const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc;
if (gd) { if (gd) {

File diff suppressed because it is too large Load diff

View file

@ -29,52 +29,67 @@
#define TINSEL_INVENTORY_H #define TINSEL_INVENTORY_H
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/events.h" // for KEYEVENT, BUTEVENT #include "tinsel/events.h" // for PLR_EVENT, PLR_EVENT
namespace Tinsel { namespace Tinsel {
class Serializer; class Serializer;
enum { enum {
INV_OPEN = -1, INV_OPEN = -1, // DW1 only
INV_CONV = 0, INV_CONV = 0,
INV_1 = 1, INV_1 = 1,
INV_2 = 2, INV_2 = 2,
INV_CONF = 3, INV_CONF = 3,
INV_MENU = 3, // DW2 constant
NUM_INV = 4,
NUM_INV = 4 // Discworld 2 constants
DW2_INV_OPEN = 5,
INV_DEFAULT = 6
}; };
/** structure of each inventory object */ /** structure of each inventory object */
struct INV_OBJECT { struct INV_OBJECT {
int32 id; // inventory objects id int32 id; // inventory objects id
SCNHANDLE hFilm; // inventory objects animation film SCNHANDLE hIconFilm; // inventory objects animation film
SCNHANDLE hScript; // inventory objects event handling script SCNHANDLE hScript; // inventory objects event handling script
int32 attribute; // inventory object's attribute int32 attribute; // inventory object's attribute
}; };
// attribute values - not a bit bit field to prevent portability problems
#define DROPCODE 0x01
#define ONLYINV1 0x02
#define ONLYINV2 0x04
#define DEFINV1 0x08
#define DEFINV2 0x10
#define PERMACONV 0x20
#define CONVENDITEM 0x40
void PopUpInventory(int invno); void PopUpInventory(int invno);
enum CONFTYPE { enum CONFTYPE {
SAVE, LOAD, QUIT, OPTION, RESTART, SOUND, CONTROLS, SUBT, TOPWIN MAIN_MENU, SAVE_MENU, LOAD_MENU, QUIT_MENU, RESTART_MENU, SOUND_MENU,
CONTROLS_MENU, SUBTITLES_MENU, HOPPER_MENU1, HOPPER_MENU2, TOP_WINDOW
}; };
void PopUpConf(CONFTYPE type); void OpenMenu(CONFTYPE type);
void Xmovement(int x); void Xmovement(int x);
void Ymovement(int y); void Ymovement(int y);
void ButtonToInventory(BUTEVENT be); void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds);
void ButtonToInventory(PLR_EVENT be);
void KeyToInventory(KEYEVENT ke); void KeyToInventory(PLR_EVENT ke);
int WhichItemHeld(void); int WhichItemHeld(void);
void HoldItem(int item); void HoldItem(int item, bool bKeepFilm = false);
void DropItem(int item); void DropItem(int item);
void AddToInventory(int invno, int icon, bool hold); void ClearInventory(int invno);
void AddToInventory(int invno, int icon, bool hold = false);
bool RemFromInventory(int invno, int icon); bool RemFromInventory(int invno, int icon);
@ -89,25 +104,34 @@ void idec_inv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
bool InventoryActive(void); bool InventoryActive(void);
void AddIconToPermanentDefaultList(int icon); void PermaConvIcon(int icon, bool bEnd = false);
void convPos(int bpos); void convPos(int bpos);
void ConvPoly(HPOLYGON hp); void ConvPoly(HPOLYGON hp);
int convIcon(void); int GetIcon(void);
void CloseDownConv(void); void CloseDownConv(void);
void convHide(bool hide); void HideConversation(bool hide);
bool convHid(void); bool ConvIsHidden(void);
enum { enum {
NOOBJECT = -1,
INV_NOICON = -1, INV_NOICON = -1,
INV_CLOSEICON = -2, INV_CLOSEICON = -2,
INV_OPENICON = -3, INV_OPENICON = -3,
INV_HELDNOTIN = -4 INV_HELDNOTIN = -4
}; };
void ConvAction(int index); enum CONV_PARAM {
CONV_DEF,
CONV_BOTTOM,
CONV_END,
CONV_TOP
};
void InventoryIconCursor(void);
void ConvAction(int index);
void SetConvDetails(CONV_PARAM fn, HPOLYGON hPoly, int ano);
void InventoryIconCursor(bool bNewItem);
void setInvWinParts(SCNHANDLE hf); void setInvWinParts(SCNHANDLE hf);
void setFlagFilms(SCNHANDLE hf); void setFlagFilms(SCNHANDLE hf);
@ -122,8 +146,6 @@ bool IsInInventory(int object, int invnum);
void KillInventory(void); void KillInventory(void);
void invObjectFilm(int object, SCNHANDLE hFilm);
void syncInvInfo(Serializer &s); void syncInvInfo(Serializer &s);
int InvGetLimit(int invno); int InvGetLimit(int invno);
@ -134,9 +156,13 @@ void InvSetSize(int invno, int MinWidth, int MinHeight,
int WhichInventoryOpen(void); int WhichInventoryOpen(void);
bool IsTopWindow(void); bool IsTopWindow(void);
bool IsConfWindow(void); bool MenuActive(void);
bool IsConvWindow(void); bool IsConvWindow(void);
void SetObjectFilm(int object, SCNHANDLE hFilm);
void ObjectEvent(CORO_PARAM, int objId, TINSEL_EVENT event, bool bWait, int myEscape, bool *result = NULL);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif /* TINSEL_INVENTRY_H */ #endif /* TINSEL_INVENTRY_H */

129
engines/tinsel/drives.cpp Normal file
View file

@ -0,0 +1,129 @@
/* 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$
*
* CD/drive handling functions
*/
#include "tinsel/drives.h"
#include "tinsel/scene.h"
#include "tinsel/tinsel.h"
#include "tinsel/sched.h"
#include "tinsel/strres.h"
namespace Tinsel {
static char currentCD = '1';
static uint32 cdFlags[] = { fCd1, fCd2, fCd3, fCd4, fCd5, fCd6, fCd7, fCd8 };
static bool bChangingCD = false;
static char nextCD = '\0';
void CdCD(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
while (bChangingCD) {
if (g_scheduler->getCurrentProcess()) {
// FIXME: CdCD gets passed a nullContext in RegisterGlobals() and
// PrimeSceneHopper(), because I didn't know how to get a proper
// context without converting the whole calling stack to CORO'd
// functions. If these functions really get called while a CD
// change is requested, this needs to be resolved.
if (coroParam == nullContext)
error("CdCD needs context!");
CORO_SLEEP(1);
} else
error("No current process in CdCD()!");
}
CORO_END_CODE;
}
int GetCurrentCD(void) {
// count from 1
return (currentCD - '1' + 1);
}
void SetCD(int flags) {
if (flags & cdFlags[currentCD - '1'])
return;
error("SetCD() problem");
}
int GetCD(int flags) {
int i;
char cd = '\0';
if (flags & cdFlags[currentCD - '1'])
return GetCurrentCD();
for (i = 0; i < 8; i++) {
if (flags & cdFlags[i]) {
cd = (char)(i + '1');
break;
}
}
assert(i != 8);
nextCD = cd;
return cd;
}
void DoCdChange(void) {
if (bChangingCD) {
_vm->_sound->closeSampleStream();
_vm->_sound->openSampleFiles();
ChangeLanguage(TextLanguage());
bChangingCD = false;
}
}
void SetNextCD(int cdNumber) {
assert(cdNumber == 1 || cdNumber == 2);
nextCD = (char)(cdNumber + '1' - 1);
}
bool GotoCD(void) {
// WORKAROUND: Somehow, CdDoChange() is called twice... Hopefully, this guard helps
if (currentCD == nextCD)
return false;
currentCD = nextCD;
/* if (bNoCD) {
strcpy(cdDirectory, hdDirectory);
cdLastBit[3] = currentCD;
strcat(cdDirectory, cdLastBit);
}
*/
bChangingCD = true;
return true;
}
} // end of namespace Tinsel

63
engines/tinsel/drives.h Normal file
View file

@ -0,0 +1,63 @@
/* 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$
*
* CD/drive handling functions
*/
#ifndef TINSEL_DRIVES_H
#define TINSEL_DRIVES_H
#include "tinsel/dw.h"
#include "tinsel/coroutine.h"
namespace Tinsel {
// flags2
#define fCd1 0x00000001L
#define fCd2 0x00000002L
#define fCd3 0x00000004L
#define fCd4 0x00000008L
#define fCd5 0x00000010L
#define fCd6 0x00000020L
#define fCd7 0x00000040L
#define fCd8 0x00000080L
#define fAllCds (fCd1|fCd2|fCd3|fCd4|fCd5|fCd6|fCd7|fCd8)
void DoCdChange(void);
void CdCD(CORO_PARAM);
int GetCurrentCD(void);
void SetCD(int flags);
int GetCD(int flags);
void SetNextCD(int cdNumber);
bool GotoCD(void);
} // end of namespace Tinsel
#endif /* TINSEL_DRIVES_H */

View file

@ -47,9 +47,8 @@ typedef int HPOLYGON;
#define MIDI_FILE "midi.dat" // all MIDI sequences #define MIDI_FILE "midi.dat" // all MIDI sequences
#define INDEX_FILENAME "index" // name of index file #define INDEX_FILENAME "index" // name of index file
#define SCNHANDLE_SHIFT 23 // amount to shift scene handles by
#define NO_SCNHANDLES 300 // number of memory handles for scenes #define NO_SCNHANDLES 300 // number of memory handles for scenes
#define MASTER_SCNHANDLE (0 << SCNHANDLE_SHIFT) // master scene memory handle #define MASTER_SCNHANDLE 0 // master scene memory handle
// the minimum value a integer number can have // the minimum value a integer number can have
#define MIN_INT (1 << (8*sizeof(int) - 1)) #define MIN_INT (1 << (8*sizeof(int) - 1))
@ -70,8 +69,7 @@ typedef int HPOLYGON;
#define FIELD_WORLD 0 #define FIELD_WORLD 0
#define FIELD_STATUS 1 #define FIELD_STATUS 1
#define ZSHIFT 10
// We don't set the Z position for print and talk text // We don't set the Z position for print and talk text
// i.e. it gets a Z position of 0 // i.e. it gets a Z position of 0
@ -101,6 +99,7 @@ typedef int HPOLYGON;
#define MAX_MOVERS 6 // Moving actors using path system #define MAX_MOVERS 6 // Moving actors using path system
#define MAX_SAVED_ACTORS 32 // Saved 'Normal' actors #define MAX_SAVED_ACTORS 32 // Saved 'Normal' actors
#define MAX_SAVED_ALIVES 512 // Saves actors'lives #define MAX_SAVED_ALIVES 512 // Saves actors'lives
#define MAX_SAVED_ACTOR_Z 512 // Saves actors' Z-ness
// Legal non-existant entrance number for LoadScene() // Legal non-existant entrance number for LoadScene()
#define NO_ENTRY_NUM (-3458) // Magic unlikely number #define NO_ENTRY_NUM (-3458) // Magic unlikely number
@ -110,13 +109,19 @@ typedef int HPOLYGON;
// Language for the resource strings // Language for the resource strings
enum LANGUAGE { enum LANGUAGE {
TXT_ENGLISH, TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH,
TXT_FRENCH, TXT_HEBREW, TXT_HUNGARIAN, TXT_JAPANESE, TXT_US,
TXT_GERMAN, NUM_LANGUAGES
TXT_ITALIAN,
TXT_SPANISH
}; };
#define MAX_READ_RETRIES 5
// Definitions used for error messages
#define FILE_IS_CORRUPT "File %s is corrupt"
#define FILE_READ_ERROR "Error reading file %s"
#define CANNOT_FIND_FILE "Cannot find file %s"
#define NO_MEM "Cannot allocate memory for %s!"
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_DW_H #endif // TINSEL_DW_H

View file

@ -41,13 +41,14 @@
#include "tinsel/polygons.h" #include "tinsel/polygons.h"
#include "tinsel/rince.h" #include "tinsel/rince.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
struct EP_INIT { struct EP_INIT {
HPOLYGON hEpoly; HPOLYGON hEpoly;
PMACTOR pActor; PMOVER pMover;
int index; int index;
}; };
@ -61,24 +62,32 @@ static void EffectProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
EP_INIT *to = (EP_INIT *)param; // get the stuff copied to process when it was created const EP_INIT *to = (const EP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
int x, y; // Lead actor position int x, y; // Lead actor position
// Run effect poly enter script // Run effect poly enter script
effRunPolyTinselCode(to->hEpoly, ENTER, to->pActor->actorID); if (TinselV2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKIN,
GetMoverId(to->pMover), false, 0));
else
effRunPolyTinselCode(to->hEpoly, WALKIN, to->pMover->actorID);
do { do {
CORO_SLEEP(1); CORO_SLEEP(1);
GetMActorPosition(to->pActor, &x, &y); GetMoverPosition(to->pMover, &x, &y);
} while (InPolygon(x, y, EFFECT) == to->hEpoly); } while (InPolygon(x, y, EFFECT) == to->hEpoly);
// Run effect poly leave script // Run effect poly leave script
effRunPolyTinselCode(to->hEpoly, LEAVE, to->pActor->actorID); if (TinselV2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, to->hEpoly, WALKOUT,
GetMoverId(to->pMover), false, 0));
else
effRunPolyTinselCode(to->hEpoly, WALKOUT, to->pMover->actorID);
SetMAinEffectPoly(to->index, false); SetMoverInEffect(to->index, false);
CORO_END_CODE; CORO_END_CODE;
} }
@ -88,7 +97,7 @@ static void EffectProcess(CORO_PARAM, const void *param) {
* it has just entered one. If it has, a process is started up to run * it has just entered one. If it has, a process is started up to run
* the polygon's Glitter code. * the polygon's Glitter code.
*/ */
static void FettleEffectPolys(int x, int y, int index, PMACTOR pActor) { static void FettleEffectPolys(int x, int y, int index, PMOVER pActor) {
HPOLYGON hPoly; HPOLYGON hPoly;
EP_INIT epi; EP_INIT epi;
@ -97,10 +106,10 @@ static void FettleEffectPolys(int x, int y, int index, PMACTOR pActor) {
hPoly = InPolygon(x, y, EFFECT); hPoly = InPolygon(x, y, EFFECT);
if (hPoly != NOPOLY) { if (hPoly != NOPOLY) {
//Just entered effect polygon //Just entered effect polygon
SetMAinEffectPoly(index, true); SetMoverInEffect(index, true);
epi.hEpoly = hPoly; epi.hEpoly = hPoly;
epi.pActor = pActor; epi.pMover = pActor;
epi.index = index; epi.index = index;
g_scheduler->createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi)); g_scheduler->createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi));
} }
@ -118,10 +127,10 @@ void EffectPolyProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
while (1) { while (1) {
for (int i = 0; i < MAX_MOVERS; i++) { for (int i = 0; i < MAX_MOVERS; i++) {
PMACTOR pActor = GetLiveMover(i); PMOVER pActor = GetLiveMover(i);
if (pActor != NULL) { if (pActor != NULL) {
int x, y; int x, y;
GetMActorPosition(pActor, &x, &y); GetMoverPosition(pActor, &x, &y);
FettleEffectPolys(x, y, i, pActor); FettleEffectPolys(x, y, i, pActor);
} }
} }

View file

@ -26,14 +26,17 @@
*/ */
#include "tinsel/actors.h" #include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/coroutine.h"
#include "tinsel/cursor.h" #include "tinsel/cursor.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/events.h" #include "tinsel/events.h"
#include "tinsel/handle.h" // For LockMem() #include "tinsel/handle.h" // For LockMem()
#include "tinsel/inventory.h" #include "tinsel/dialogs.h"
#include "tinsel/move.h" // For walking lead actor #include "tinsel/move.h" // For walking lead actor
#include "tinsel/pcode.h" // For Interpret() #include "tinsel/pcode.h" // For Interpret()
#include "tinsel/pdisplay.h"
#include "tinsel/pid.h" #include "tinsel/pid.h"
#include "tinsel/polygons.h" #include "tinsel/polygons.h"
#include "tinsel/rince.h" // For walking lead actor #include "tinsel/rince.h" // For walking lead actor
@ -41,6 +44,7 @@
#include "tinsel/scroll.h" // For DontScrollCursor() #include "tinsel/scroll.h" // For DontScrollCursor()
#include "tinsel/timers.h" // DwGetCurrentTime() #include "tinsel/timers.h" // DwGetCurrentTime()
#include "tinsel/tinlib.h" // For control() #include "tinsel/tinlib.h" // For control()
#include "tinsel/tinsel.h"
#include "tinsel/token.h" #include "tinsel/token.h"
namespace Tinsel { namespace Tinsel {
@ -51,22 +55,25 @@ namespace Tinsel {
extern int GetTaggedActor(void); extern int GetTaggedActor(void);
extern HPOLYGON GetTaggedPoly(void); extern HPOLYGON GetTaggedPoly(void);
//----------------- EXTERNAL GLOBAL DATA --------------------- //----------------- EXTERNAL GLOBAL DATA ---------------------
extern bool bEnableF1; extern bool bEnableMenu;
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
static int userEvents = 0; // Whenever a button or a key comes in
static uint32 lastUserEvent = 0; // Time it hapenned static uint32 lastUserEvent = 0; // Time it hapenned
static int butEvents = 0; // Single or double, left or right. Or escape key. static int leftEvents = 0; // Single or double, left or right. Or escape key.
static int escEvents = 0; // Escape key static int escEvents = 1; // Escape key
static int userEvents = 0; // Whenever a button or a key comes in
static int eCount = 0; static int eCount = 0;
static int controlState;
static bool bStartOff;
static int controlX, controlY;
static bool bProvNotProcessed = false;
/** /**
* Gets called before each schedule, only 1 user action per schedule * Gets called before each schedule, only 1 user action per schedule
* is allowed. * is allowed.
@ -87,12 +94,12 @@ void IncUserEvents(void) {
* If this is a double click, the process from the waiting single click * If this is a double click, the process from the waiting single click
* gets killed. * gets killed.
*/ */
void AllowDclick(CORO_PARAM, BUTEVENT be) { void AllowDclick(CORO_PARAM, PLR_EVENT be) {
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
if (be == BE_SLEFT) { if (be == PLR_SLEFT) {
GetToken(TOKEN_LEFT_BUT); GetToken(TOKEN_LEFT_BUT);
CORO_SLEEP(dclickSpeed+1); CORO_SLEEP(dclickSpeed+1);
FreeToken(TOKEN_LEFT_BUT); FreeToken(TOKEN_LEFT_BUT);
@ -103,94 +110,120 @@ void AllowDclick(CORO_PARAM, BUTEVENT be) {
break; break;
} else if (be == BE_DLEFT) { } else if (be == PLR_DLEFT) {
GetToken(TOKEN_LEFT_BUT); GetToken(TOKEN_LEFT_BUT);
FreeToken(TOKEN_LEFT_BUT); FreeToken(TOKEN_LEFT_BUT);
} }
CORO_END_CODE; CORO_END_CODE;
} }
/**
* Re-enables user control
*/
void ControlOn(void) {
if (!TinselV2) {
Control(CONTROL_ON);
return;
}
bEnableMenu = false;
if (controlState == CONTROL_OFF) {
// Control is on
controlState = CONTROL_ON;
// Restore cursor to where it was
if (bStartOff == true)
bStartOff = false;
else
SetCursorXY(controlX, controlY);
// Re-instate cursor
UnHideCursor();
// Turn tags back on
if (!InventoryActive())
EnableTags();
}
}
/**
* Takes control from the user
*/
void ControlOff(void) {
if (!TinselV2) {
Control(CONTROL_ON);
return;
}
bEnableMenu = false;
if (controlState == CONTROL_ON) {
// Control is off
controlState = CONTROL_OFF;
// Store cursor position
GetCursorXY(&controlX, &controlY, true);
// Blank out cursor
DwHideCursor();
// Switch off tags
DisableTags();
}
}
/**
* Prevent tags and cursor re-appearing
*/
void ControlStartOff(void) {
if (!TinselV2) {
Control(CONTROL_STARTOFF);
return;
}
bEnableMenu = false;
// Control is off
controlState = CONTROL_OFF;
// Blank out cursor
DwHideCursor();
// Switch off tags
DisableTags();
bStartOff = true;
}
/** /**
* Take control from player, if the player has it. * Take control from player, if the player has it.
* Return TRUE if control taken, FALSE if not. * Return TRUE if control taken, FALSE if not.
*/ */
bool GetControl(int param) { bool GetControl(int param) {
if (TestToken(TOKEN_CONTROL)) { if (TinselV2)
control(param); return GetControl();
else if (TestToken(TOKEN_CONTROL)) {
Control(param);
return true; return true;
} else } else
return false; return false;
} }
struct TP_INIT { bool GetControl(void) {
HPOLYGON hPoly; // Polygon if (controlState == CONTROL_ON) {
USER_EVENT event; // Trigerring event ControlOff();
BUTEVENT bev; // To allow for double clicks return true;
bool take_control; // Set if control should be taken } else
// while code is running. return false;
int actor;
};
/**
* Runs glitter code associated with a polygon.
*/
static void PolyTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
bool took_control; // Set if this function takes control
CORO_END_CONTEXT(_ctx);
TP_INIT *to = (TP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx);
CORO_INVOKE_1(AllowDclick, to->bev); // May kill us if single click
// Control may have gone off during AllowDclick()
if (!TestToken(TOKEN_CONTROL)
&& (to->event == WALKTO || to->event == ACTION || to->event == LOOK))
CORO_KILL_SELF();
// Take control, if requested
if (to->take_control)
_ctx->took_control = GetControl(CONTROL_OFF);
else
_ctx->took_control = false;
// Hide conversation if appropriate
if (to->event == CONVERSE)
convHide(true);
// Run the code
_ctx->pic = InitInterpretContext(GS_POLYGON, getPolyScript(to->hPoly), to->event, to->hPoly, to->actor, NULL);
CORO_INVOKE_1(Interpret, _ctx->pic);
// Free control if we took it
if (_ctx->took_control)
control(CONTROL_ON);
// Restore conv window if applicable
if (to->event == CONVERSE)
convHide(false);
CORO_END_CODE;
} }
/** bool ControlIsOn(void) {
* Runs glitter code associated with a polygon. if (TinselV2)
*/ return (controlState == CONTROL_ON);
void RunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, BUTEVENT be, bool tc) {
TP_INIT to = { hPoly, event, be, tc, 0 };
g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to)); return TestToken(TOKEN_CONTROL);
}
void effRunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, int actor) {
TP_INIT to = { hPoly, event, BE_NONE, false, actor };
g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
} }
//----------------------------------------------------------------------- //-----------------------------------------------------------------------
@ -206,22 +239,33 @@ struct WP_INIT {
static void WalkProcess(CORO_PARAM, const void *param) { static void WalkProcess(CORO_PARAM, const void *param) {
// COROUTINE // COROUTINE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
PMACTOR pActor; PMOVER pMover;
int thisWalk;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
WP_INIT *to = (WP_INIT *)param; // get the co-ordinates - copied to process when it was created const WP_INIT *to = (const WP_INIT *)param; // get the co-ordinates - copied to process when it was created
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
_ctx->pActor = GetMover(LEAD_ACTOR); _ctx->pMover = GetMover(LEAD_ACTOR);
if (_ctx->pActor->MActorState == NORM_MACTOR) {
assert(_ctx->pActor->hCpath != NOPOLY); // Lead actor is not in a path
GetToken(TOKEN_LEAD); if (TinselV2 && MoverIs(_ctx->pMover) && !MoverIsSWalking(_ctx->pMover)) {
SetActorDest(_ctx->pActor, to->x, to->y, false, 0); assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
_ctx->thisWalk = SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
DontScrollCursor(); DontScrollCursor();
while (MAmoving(_ctx->pActor)) while (MoverMoving(_ctx->pMover) && (_ctx->thisWalk == GetWalkNumber(_ctx->pMover)))
CORO_SLEEP(1);
} else if (!TinselV2 && _ctx->pMover->bActive) {
assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
GetToken(TOKEN_LEAD);
SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
DontScrollCursor();
while (MoverMoving(_ctx->pMover))
CORO_SLEEP(1); CORO_SLEEP(1);
FreeToken(TOKEN_LEAD); FreeToken(TOKEN_LEAD);
@ -230,7 +274,7 @@ static void WalkProcess(CORO_PARAM, const void *param) {
CORO_END_CODE; CORO_END_CODE;
} }
void walkto(int x, int y) { void WalkTo(int x, int y) {
WP_INIT to = { x, y }; WP_INIT to = { x, y };
g_scheduler->createProcess(PID_TCODE, WalkProcess, &to, sizeof(to)); g_scheduler->createProcess(PID_TCODE, WalkProcess, &to, sizeof(to));
@ -240,7 +284,7 @@ void walkto(int x, int y) {
* Run appropriate actor or polygon glitter code. * Run appropriate actor or polygon glitter code.
* If none, and it's a WALKTO event, do a walk. * If none, and it's a WALKTO event, do a walk.
*/ */
static void User_Event(USER_EVENT uEvent, BUTEVENT be) { static void ProcessUserEvent(TINSEL_EVENT uEvent, const Common::Point &coOrds, PLR_EVENT be = PLR_NOEVENT) {
int actor; int actor;
int aniX, aniY; int aniX, aniY;
HPOLYGON hPoly; HPOLYGON hPoly;
@ -249,19 +293,34 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
if (++eCount != 1) if (++eCount != 1)
return; return;
if ((actor = GetTaggedActor()) != 0) if ((actor = GetTaggedActor()) != 0) {
actorEvent(actor, uEvent, be); // Event for a tagged actor
else if ((hPoly = GetTaggedPoly()) != NOPOLY) if (TinselV2)
ActorEvent(nullContext, actor, uEvent, false, 0);
else
ActorEvent(actor, uEvent, be);
} else if ((hPoly = GetTaggedPoly()) != NOPOLY) {
// Event for active tagged polygon
if (!TinselV2)
RunPolyTinselCode(hPoly, uEvent, be, false); RunPolyTinselCode(hPoly, uEvent, be, false);
else { else if (uEvent != PROV_WALKTO)
PolygonEvent(nullContext, hPoly, uEvent, 0, false, 0);
} else {
GetCursorXY(&aniX, &aniY, true); GetCursorXY(&aniX, &aniY, true);
// There could be a poly involved which has no tag. // There could be a poly involved which has no tag.
if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY ||
|| (hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY) { (!TinselV2 && ((hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY))) {
if (TinselV2 && (uEvent != PROV_WALKTO))
PolygonEvent(nullContext, hPoly, uEvent, 0, false, 0);
else if (!TinselV2)
RunPolyTinselCode(hPoly, uEvent, be, false); RunPolyTinselCode(hPoly, uEvent, be, false);
} else if (uEvent == WALKTO) } else if ((uEvent == PROV_WALKTO) || (uEvent == WALKTO)) {
walkto(aniX, aniY); if (TinselV2)
ProcessedProvisional();
WalkTo(aniX, aniY);
}
} }
} }
@ -269,137 +328,155 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
/** /**
* ProcessButEvent * ProcessButEvent
*/ */
void ProcessButEvent(BUTEVENT be) { void ProcessButEvent(PLR_EVENT be) {
IncUserEvents();
if (bSwapButtons) { if (bSwapButtons) {
switch (be) { switch (be) {
case BE_SLEFT: case PLR_SLEFT:
be = BE_SRIGHT; be = PLR_SRIGHT;
break; break;
case BE_DLEFT: case PLR_DLEFT:
be = BE_DRIGHT; be = PLR_DRIGHT;
break; break;
case BE_SRIGHT: case PLR_SRIGHT:
be = BE_SLEFT; be = PLR_SLEFT;
break; break;
case BE_DRIGHT: case PLR_DRIGHT:
be = BE_DLEFT; be = PLR_DLEFT;
break; break;
case BE_LDSTART: case PLR_DRAG1_START:
be = BE_RDSTART; be = PLR_DRAG2_START;
break; break;
case BE_LDEND: case PLR_DRAG1_END:
be = BE_RDEND; be = PLR_DRAG2_END;
break; break;
case BE_RDSTART: case PLR_DRAG2_START:
be = BE_LDSTART; be = PLR_DRAG1_START;
break; break;
case BE_RDEND: case PLR_DRAG2_END:
be = BE_LDEND; be = PLR_DRAG1_END;
break; break;
default: default:
break; break;
} }
} }
// if (be == BE_SLEFT || be == BE_DLEFT || be == BE_SRIGHT || be == BE_DRIGHT) PlayerEvent(be, _vm->getMousePosition());
if (be == BE_SLEFT || be == BE_SRIGHT)
butEvents++;
if (!TestToken(TOKEN_CONTROL) && be != BE_LDEND)
return;
if (InventoryActive()) {
ButtonToInventory(be);
} else {
switch (be) {
case BE_SLEFT:
User_Event(WALKTO, BE_SLEFT);
break;
case BE_DLEFT:
User_Event(ACTION, BE_DLEFT);
break;
case BE_SRIGHT:
User_Event(LOOK, BE_SRIGHT);
break;
default:
break;
}
}
} }
/** /**
* ProcessKeyEvent * ProcessKeyEvent
*/ */
void ProcessKeyEvent(KEYEVENT ke) { void ProcessKeyEvent(PLR_EVENT ke) {
// Pass the keyboard event to the player event handler
int xp, yp;
GetCursorXYNoWait(&xp, &yp, true);
const Common::Point mousePos(xp, yp);
PlayerEvent(ke, mousePos);
}
#define REAL_ACTION_CHECK if (TinselV2) { \
if (DwGetCurrentTime() - lastRealAction < 4) return; \
lastRealAction = DwGetCurrentTime(); \
}
/**
* Main interface point for specifying player atcions
*/
void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
// Logging of player actions
const char *actionList[] = {
"PLR_PROV_WALKTO", "PLR_WALKTO", "PLR_LOOK", "PLR_ACTION", "PLR_ESCAPE",
"PLR_MENU", "PLR_QUIT", "PLR_PGUP", "PLR_PGDN", "PLR_HOME", "PLR_END",
"PLR_DRAG1_START", "PLR_DRAG1_END", "PLR_DRAG2_START", "PLR_DRAG2_END",
"PLR_JUMP", "PLR_NOEVENT"};
debugC(DEBUG_BASIC, kTinselDebugActions, "%s - (%d,%d)",
actionList[pEvent], coOrds.x, coOrds.y);
static uint32 lastRealAction = 0;
// This stuff to allow F1 key during startup. // This stuff to allow F1 key during startup.
if (bEnableF1 && ke == OPTION_KEY) if (bEnableMenu && pEvent == PLR_MENU)
control(CONTROL_ON); Control(CONTROL_ON);
else else
IncUserEvents(); IncUserEvents();
if (ke == ESC_KEY) { if (pEvent == PLR_ESCAPE) {
escEvents++; ++escEvents;
butEvents++; // Yes, I do mean this ++leftEvents; // Yes, I do mean this
} else if ((pEvent == PLR_PROV_WALKTO)
|| (pEvent == PLR_WALKTO)
|| (pEvent == PLR_LOOK)
|| (pEvent == PLR_ACTION)) {
++leftEvents;
} }
// FIXME: This comparison is weird - I added (BUTEVENT) cast for now to suppress warning // Only allow events if player control is on
if (!TestToken(TOKEN_CONTROL) && (BUTEVENT)ke != BE_LDEND) if (!ControlIsOn() && (pEvent != PLR_DRAG1_END))
return; return;
switch (ke) { if (TinselV2 && InventoryActive()) {
case QUIT_KEY: int x, y;
PopUpConf(QUIT); PlayfieldGetPos(FIELD_WORLD, &x, &y);
EventToInventory(pEvent, Common::Point(coOrds.x - x, coOrds.y - y));
return;
}
switch (pEvent) {
case PLR_QUIT:
OpenMenu(QUIT_MENU);
break; break;
case OPTION_KEY: case PLR_MENU:
PopUpConf(OPTION); OpenMenu(MAIN_MENU);
break; break;
case SAVE_KEY: case PLR_JUMP:
PopUpConf(SAVE); OpenMenu(HOPPER_MENU1);
break; break;
case LOAD_KEY: case PLR_SAVE:
PopUpConf(LOAD); OpenMenu(SAVE_MENU);
break; break;
case WALKTO_KEY: case PLR_LOAD:
if (InventoryActive()) OpenMenu(LOAD_MENU);
ButtonToInventory(BE_SLEFT); break;
case PLR_PROV_WALKTO: // Provisional WALKTO !
ProcessUserEvent(PROV_WALKTO, coOrds);
break;
case PLR_WALKTO:
REAL_ACTION_CHECK;
if (TinselV2 || !InventoryActive())
ProcessUserEvent(WALKTO, coOrds, PLR_SLEFT);
else else
User_Event(WALKTO, BE_NONE); EventToInventory(PLR_SLEFT, coOrds);
break; break;
case ACTION_KEY: case PLR_ACTION:
if (InventoryActive()) REAL_ACTION_CHECK;
ButtonToInventory(BE_DLEFT);
if (TinselV2 || !InventoryActive())
ProcessUserEvent(ACTION, coOrds, PLR_DLEFT);
else else
User_Event(ACTION, BE_NONE); EventToInventory(PLR_DLEFT, coOrds);
break; break;
case LOOK_KEY: case PLR_LOOK:
if (InventoryActive()) REAL_ACTION_CHECK;
ButtonToInventory(BE_SRIGHT);
if (TinselV2 || !InventoryActive())
ProcessUserEvent(LOOK, coOrds, PLR_SRIGHT);
else else
User_Event(LOOK, BE_NONE); EventToInventory(PLR_SRIGHT, coOrds);
break;
case ESC_KEY:
case PGUP_KEY:
case PGDN_KEY:
case HOME_KEY:
case END_KEY:
if (InventoryActive())
KeyToInventory(ke);
break; break;
default: default:
if (InventoryActive())
EventToInventory(pEvent, coOrds);
break; break;
} }
} }
@ -407,7 +484,6 @@ void ProcessKeyEvent(KEYEVENT ke) {
/** /**
* For ESCapable Glitter sequences * For ESCapable Glitter sequences
*/ */
int GetEscEvents(void) { int GetEscEvents(void) {
return escEvents; return escEvents;
} }
@ -415,15 +491,21 @@ int GetEscEvents(void) {
/** /**
* For cutting short talk()s etc. * For cutting short talk()s etc.
*/ */
int GetLeftEvents(void) { int GetLeftEvents(void) {
return butEvents; return leftEvents;
}
bool LeftEventChange(int myleftEvent) {
if (leftEvents != myleftEvent) {
ProcessedProvisional();
return true;
} else
return false;
} }
/** /**
* For waitkey() Glitter function * For waitkey() Glitter function
*/ */
int getUserEvents(void) { int getUserEvents(void) {
return userEvents; return userEvents;
} }
@ -436,4 +518,154 @@ void resetUserEventTime(void) {
lastUserEvent = DwGetCurrentTime(); lastUserEvent = DwGetCurrentTime();
} }
struct PTP_INIT {
HPOLYGON hPoly; // Polygon
TINSEL_EVENT event; // Trigerring event
PLR_EVENT bev; // To allow for double clicks
bool take_control; // Set if control should be taken
// while code is running.
int actor;
PINT_CONTEXT pic;
};
/**
* Runs glitter code associated with a polygon.
*/
void PolyTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
bool bTookControl; // Set if this function takes control
CORO_END_CONTEXT(_ctx);
const PTP_INIT *to = (const PTP_INIT *)param; // get the stuff copied to process when it was created
CORO_BEGIN_CODE(_ctx);
if (TinselV2) {
// Take control for CONVERSE events
if (to->event == CONVERSE) {
_ctx->bTookControl = GetControl();
HideConversation(true);
} else
_ctx->bTookControl = false;
CORO_INVOKE_1(Interpret, to->pic);
// Restore conv window if applicable
if (to->event == CONVERSE) {
// Free control if we took it
if (_ctx->bTookControl)
ControlOn();
HideConversation(false);
}
} else {
CORO_INVOKE_1(AllowDclick, to->bev); // May kill us if single click
// Control may have gone off during AllowDclick()
if (!TestToken(TOKEN_CONTROL)
&& (to->event == WALKTO || to->event == ACTION || to->event == LOOK))
CORO_KILL_SELF();
// Take control, if requested
if (to->take_control)
_ctx->bTookControl = GetControl(CONTROL_OFF);
else
_ctx->bTookControl = false;
// Hide conversation if appropriate
if (to->event == CONVERSE)
HideConversation(true);
// Run the code
_ctx->pic = InitInterpretContext(GS_POLYGON, GetPolyScript(to->hPoly), to->event, to->hPoly, to->actor, NULL);
CORO_INVOKE_1(Interpret, _ctx->pic);
// Free control if we took it
if (_ctx->bTookControl)
Control(CONTROL_ON);
// Restore conv window if applicable
if (to->event == CONVERSE)
HideConversation(false);
}
CORO_END_CODE;
}
/**
* Run the Polygon process with the given event
*/
void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
int myEscape, bool *result) {
CORO_BEGIN_CONTEXT;
PTP_INIT to;
PPROCESS pProc;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (result) *result = false;
_ctx->to.hPoly = -1;
_ctx->to.event = tEvent;
_ctx->to.pic = InitInterpretContext(GS_POLYGON,
GetPolyScript(hPoly),
tEvent,
hPoly, // Polygon
actor, // Actor
NULL, // No Object
myEscape);
if (_ctx->to.pic != NULL) {
_ctx->pProc = g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &_ctx->to, sizeof(_ctx->to));
AttachInterpret(_ctx->to.pic, _ctx->pProc);
if (bWait)
CORO_INVOKE_2(WaitInterpret,_ctx->pProc, result);
}
CORO_END_CODE;
}
/**
* Runs glitter code associated with a polygon.
*/
void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc) {
PTP_INIT to = { hPoly, event, be, tc, 0, NULL };
assert(!TinselV2);
g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
}
void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor) {
PTP_INIT to = { hPoly, event, PLR_NOEVENT, false, actor, NULL };
assert(!TinselV2);
g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
}
/**
* If provisional event was processed, calling this prevents the
* subsequent 'real' event.
*/
void ProcessedProvisional(void) {
bProvNotProcessed = false;
}
/**
* Resets the bProvNotProcessed flag
*/
void ProvNotProcessed(void) {
bProvNotProcessed = true;
}
bool GetProvNotProcessed() {
return bProvNotProcessed;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -29,49 +29,99 @@
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/coroutine.h" #include "tinsel/coroutine.h"
#include "common/rect.h"
namespace Tinsel { namespace Tinsel {
/*
enum BUTEVENT { enum BUTEVENT {
BE_NONE, BE_SLEFT, BE_DLEFT, BE_SRIGHT, BE_DRIGHT, PLR_NOEVENT, PLR_SLEFT, PLR_DLEFT, PLR_SRIGHT, PLR_DRIGHT,
BE_LDSTART, BE_LDEND, BE_RDSTART, BE_RDEND, PLR_DRAG1_START, PLR_DRAG1_END, PLR_DRAG2_START, PLR_DRAG2_END,
BE_UNKNOWN PLR_UNKNOWN
}; };
enum KEYEVENT { enum KEYEVENT {
ESC_KEY, QUIT_KEY, SAVE_KEY, LOAD_KEY, OPTION_KEY, PLR_ESCAPE, PLR_QUIT, PLR_SAVE, PLR_LOAD, PLR_MENU,
PGUP_KEY, PGDN_KEY, HOME_KEY, END_KEY, PLR_PGUP, PLR_PGDN, PLR_HOME, PLR_END,
WALKTO_KEY, ACTION_KEY, LOOK_KEY, PLR_WALKTO, PLR_ACTION, PLR_LOOK,
NOEVENT_KEY NOEVENT_KEY
}; };*/
enum PLR_EVENT {
// action list
PLR_PROV_WALKTO = 0, // Provisional WALKTO !
PLR_WALKTO = 1,
PLR_LOOK = 2,
PLR_ACTION = 3,
PLR_ESCAPE = 4,
PLR_MENU = 5,
PLR_QUIT = 6,
PLR_PGUP = 7,
PLR_PGDN = 8,
PLR_HOME = 9,
PLR_END = 10,
PLR_DRAG1_START = 11,
PLR_DRAG1_END = 12,
PLR_DRAG2_START = 13,
PLR_DRAG2_END = 14,
PLR_JUMP = 15, // Call up scene hopper
PLR_NOEVENT = 16,
PLR_SAVE = 17,
PLR_LOAD = 18,
// Aliases used for DW1 actions
PLR_SLEFT = PLR_WALKTO,
PLR_DLEFT = PLR_ACTION,
PLR_SRIGHT = PLR_LOOK,
PLR_DRIGHT = PLR_NOEVENT,
PLR_UNKNOWN = PLR_NOEVENT
} ;
/** /**
* Reasons for running Glitter code. * Reasons for running Glitter code.
* Do not re-order these as equivalent CONSTs are defined in the master * Do not re-order these as equivalent CONSTs are defined in the master
* scene Glitter source file for testing against the event() library function. * scene Glitter source file for testing against the event() library function.
*
* Note: DW2 renames ENTER & LEAVE to WALKIN & WALKOUT, and has a new LEAVE event
*/ */
enum USER_EVENT { enum TINSEL_EVENT {
POINTED, WALKTO, ACTION, LOOK, NOEVENT, STARTUP, CLOSEDOWN, POINTED, UNPOINT, WALKIN, WALKOUT,
ENTER, LEAVE, STARTUP, CONVERSE, PICKUP, PUTDOWN, WALKTO, LOOK, ACTION, CONVERSE, SHOWEVENT,
UNPOINT, PUTDOWN, HIDEEVENT, TALKING, ENDTALK, LEAVE_T2, RESTORE, PROV_WALKTO
NOEVENT
}; };
enum TINSEL1_EVENT {
T1_POINTED, T1_WALKTO, T1_ACTION, T1_LOOK, T1_ENTER, T1_LEAVE, T1_STARTUP, T1_CONVERSE,
T1_UNPOINT, T1_PUTDOWN, T1_NOEVENT
};
void AllowDclick(CORO_PARAM, BUTEVENT be); const TINSEL1_EVENT TINSEL1_EVENT_MAP[] = {
T1_NOEVENT, T1_STARTUP, T1_NOEVENT, T1_POINTED, T1_UNPOINT, T1_ENTER, T1_LEAVE,
T1_NOEVENT, T1_PUTDOWN, T1_WALKTO, T1_LOOK, T1_ACTION, T1_CONVERSE, T1_NOEVENT,
T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT, T1_NOEVENT
};
void AllowDclick(CORO_PARAM, PLR_EVENT be);
bool GetControl(int param); bool GetControl(int param);
bool GetControl(void);
bool ControlIsOn(void);
void ControlOn(void);
void ControlOff(void);
void ControlStartOff(void);
void RunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, BUTEVENT be, bool tc); void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc);
void effRunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, int actor); void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor);
void ProcessButEvent(BUTEVENT be); void ProcessButEvent(PLR_EVENT be);
void ProcessKeyEvent(KEYEVENT ke); void ProcessKeyEvent(PLR_EVENT ke);
int GetEscEvents(void); int GetEscEvents(void);
int GetLeftEvents(void); int GetLeftEvents(void);
bool LeftEventChange(int myleftEvent);
int getUserEvents(void); int getUserEvents(void);
uint32 getUserEventTime(void); uint32 getUserEventTime(void);
@ -79,6 +129,16 @@ void resetUserEventTime(void);
void ResetEcount(void); void ResetEcount(void);
void PolygonEvent(CORO_PARAM, HPOLYGON hPoly, TINSEL_EVENT tEvent, int actor, bool bWait,
int myEscape, bool *result = NULL);
void PlayerEvent(PLR_EVENT pEvent, const Common::Point &coOrds);
void ProcessedProvisional(void);
void ProvNotProcessed(void);
bool GetProvNotProcessed();
} // end of namespace Tinsel } // end of namespace Tinsel
#endif /* TINSEL_EVENTS_H */ #endif /* TINSEL_EVENTS_H */

View file

@ -24,11 +24,14 @@
* Palette Fader and Flasher processes. * Palette Fader and Flasher processes.
*/ */
#include "tinsel/pid.h" // list of all process IDs #include "tinsel/actors.h"
#include "tinsel/sched.h" // scheduler defs
#include "tinsel/faders.h" // fader defs #include "tinsel/faders.h" // fader defs
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/palette.h" // Palette Manager defs #include "tinsel/palette.h" // Palette Manager defs
#include "tinsel/pid.h" // list of all process IDs
#include "tinsel/sched.h" // scheduler defs
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -40,9 +43,7 @@ struct FADE {
// fixed point fade multiplier tables // fixed point fade multiplier tables
//const long fadeout[] = {0xf000, 0xd000, 0xb000, 0x9000, 0x7000, 0x5000, 0x3000, 0x1000, 0, -1}; //const long fadeout[] = {0xf000, 0xd000, 0xb000, 0x9000, 0x7000, 0x5000, 0x3000, 0x1000, 0, -1};
const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
//const long fadein[] = {0, 0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xb000, 0xd000, 0x10000L, -1}; //const long fadein[] = {0, 0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xb000, 0xd000, 0x10000L, -1};
const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
/** /**
* Scale 'colour' by the fixed point colour multiplier 'colourMult' * Scale 'colour' by the fixed point colour multiplier 'colourMult'
@ -70,8 +71,18 @@ static COLORREF ScaleColour(COLORREF colour, uint32 colourMult) {
*/ */
static void FadePalette(COLORREF *pNew, COLORREF *pOrig, int numColours, uint32 mult) { static void FadePalette(COLORREF *pNew, COLORREF *pOrig, int numColours, uint32 mult) {
for (int i = 0; i < numColours; i++, pNew++, pOrig++) { for (int i = 0; i < numColours; i++, pNew++, pOrig++) {
if (!TinselV2)
// apply multiplier to RGB components // apply multiplier to RGB components
*pNew = ScaleColour(*pOrig, mult); *pNew = ScaleColour(*pOrig, mult);
else if (i == (TalkColour() - 1)) {
*pNew = GetTalkColourRef();
*pNew = ScaleColour(*pNew, mult);
} else if (SysVar(SV_TAGCOLOUR) && i == (SysVar(SV_TAGCOLOUR) - 1)) {
*pNew = GetTagColorRef();
*pNew = ScaleColour(*pNew, mult);
} else {
*pNew = ScaleColour(*pOrig, mult);
}
} }
} }
@ -89,10 +100,14 @@ static void FadeProcess(CORO_PARAM, const void *param) {
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
// get the fade data structure - copied to process when it was created // get the fade data structure - copied to process when it was created
FADE *pFade = (FADE *)param; const FADE *pFade = (const FADE *)param;
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
if (TinselV2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, true);
// get pointer to palette - reduce pointer indirection a bit // get pointer to palette - reduce pointer indirection a bit
_ctx->pPalette = (PALETTE *)LockMem(pFade->pPalQ->hPal); _ctx->pPalette = (PALETTE *)LockMem(pFade->pPalQ->hPal);
@ -100,6 +115,10 @@ static void FadeProcess(CORO_PARAM, const void *param) {
// go through all multipliers in table - until a negative entry // go through all multipliers in table - until a negative entry
// fade palette using next multiplier // fade palette using next multiplier
if (TinselV2)
FadePalette(_ctx->fadeRGB, pFade->pPalQ->palRGB,
FROM_LE_32(pFade->pPalQ->numColours), (uint32) *_ctx->pColMult);
else
FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB, FadePalette(_ctx->fadeRGB, _ctx->pPalette->palRGB,
FROM_LE_32(_ctx->pPalette->numColours), (uint32) *_ctx->pColMult); FROM_LE_32(_ctx->pPalette->numColours), (uint32) *_ctx->pColMult);
@ -110,6 +129,10 @@ static void FadeProcess(CORO_PARAM, const void *param) {
CORO_SLEEP(1); CORO_SLEEP(1);
} }
if (TinselV2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, false);
CORO_END_CODE; CORO_END_CODE;
} }
@ -122,6 +145,13 @@ static void FadeProcess(CORO_PARAM, const void *param) {
static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) { static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) {
PALQ *pPal; // palette manager iterator PALQ *pPal; // palette manager iterator
if (TinselV2) {
// The is only ever one cuncurrent fade
// But this could be a fade out and the fade in is still going!
g_scheduler->killMatchingProcess(PID_FADER);
NoFadingPalettes();
}
// create a process for each palette in the palette queue // create a process for each palette in the palette queue
for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) { for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) {
bool bFade = true; bool bFade = true;
@ -154,22 +184,61 @@ static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) {
} }
} }
/**
* Fades a list of palettes down to black.
* 'noFadeTable' is a NULL terminated list of palettes not to fade.
*/
void FadeOutMedium(SCNHANDLE noFadeTable[]) {
// Fixed point fade multiplier table
static const long fadeout[] = {0xea00, 0xd000, 0xb600, 0x9c00,
0x8200, 0x6800, 0x4e00, 0x3400, 0x1a00, 0, -1};
// call generic fader
Fader(fadeout, noFadeTable);
}
/** /**
* Fades a list of palettes down to black. * Fades a list of palettes down to black.
* @param noFadeTable A NULL terminated list of palettes not to fade. * @param noFadeTable A NULL terminated list of palettes not to fade.
*/ */
void FadeOutFast(SCNHANDLE noFadeTable[]) { void FadeOutFast(SCNHANDLE noFadeTable[]) {
// Fixed point fade multiplier table
static const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
// call generic fader // call generic fader
Fader(fadeout, noFadeTable); Fader(fadeout, noFadeTable);
} }
/**
* Fades a list of palettes from black to their current colours.
* 'noFadeTable' is a NULL terminated list of palettes not to fade.
*/
void FadeInMedium(SCNHANDLE noFadeTable[]) {
// Fade multiplier table
static const long fadein[] = {0, 0x1a00, 0x3400, 0x4e00, 0x6800,
0x8200, 0x9c00, 0xb600, 0xd000, 0xea00, 0x10000L, -1};
// call generic fader
Fader(fadein, noFadeTable);
}
/** /**
* Fades a list of palettes from black to their current colours. * Fades a list of palettes from black to their current colours.
* @param noFadeTable A NULL terminated list of palettes not to fade. * @param noFadeTable A NULL terminated list of palettes not to fade.
*/ */
void FadeInFast(SCNHANDLE noFadeTable[]) { void FadeInFast(SCNHANDLE noFadeTable[]) {
// Fade multiplier table
static const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
// call generic fader // call generic fader
Fader(fadein, noFadeTable); Fader(fadein, noFadeTable);
} }
void PokeInTagColour(void) {
if (SysVar(SV_TAGCOLOUR)) {
static COLORREF c = GetActorRGB(-1);
UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
}
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -28,16 +28,15 @@
#define TINSEL_FADERS_H #define TINSEL_FADERS_H
#include "tinsel/dw.h" // for SCNHANDLE #include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
enum { /**
/**
* Number of iterations in a fade out. * Number of iterations in a fade out.
* Must match which FadeOut() is in use.
*/ */
COUNTOUT_COUNT = 6 // FIXME: There seems to be some confusion in Tinsel 2 whether this should be 9 or 6
}; #define COUNTOUT_COUNT 6
/*----------------------------------------------------------------------*\ /*----------------------------------------------------------------------*\
|* Fader Function Prototypes *| |* Fader Function Prototypes *|
@ -47,8 +46,11 @@ enum {
// should not be faded. This parameter can be // should not be faded. This parameter can be
// NULL - fade all palettes. // NULL - fade all palettes.
void FadeOutMedium(SCNHANDLE noFadeTable[]);
void FadeOutFast(SCNHANDLE noFadeTable[]); void FadeOutFast(SCNHANDLE noFadeTable[]);
void FadeInMedium(SCNHANDLE noFadeTable[]);
void FadeInFast(SCNHANDLE noFadeTable[]); void FadeInFast(SCNHANDLE noFadeTable[]);
void PokeInTagColour(void);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -22,11 +22,14 @@
* $Id$ * $Id$
*/ */
#include "tinsel/actors.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/font.h" #include "tinsel/font.h"
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h" #include "tinsel/text.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -35,48 +38,63 @@ namespace Tinsel {
static char tBuffer[TBUFSZ]; static char tBuffer[TBUFSZ];
static SCNHANDLE hTagFont = 0, hTalkFont = 0; static SCNHANDLE hTagFont = 0, hTalkFont = 0;
static SCNHANDLE hRegularTalkFont = 0, hRegularTagFont = 0;
/** /**
* Return address of tBuffer * Return address of tBuffer
*/ */
char *tBufferAddr() { char *TextBufferAddr() {
return tBuffer; return tBuffer;
} }
/** /**
* Return hTagFont handle. * Return hTagFont handle.
*/ */
SCNHANDLE hTagFontHandle() { SCNHANDLE GetTagFontHandle() {
return hTagFont; return hTagFont;
} }
/** /**
* Return hTalkFont handle. * Return hTalkFont handle.
*/ */
SCNHANDLE hTalkFontHandle() { SCNHANDLE GetTalkFontHandle() {
return hTalkFont; return hTalkFont;
} }
/** /**
* Called from dec_tagfont() Glitter function. Store the tag font handle. * Called from dec_tagfont() Glitter function. Store the tag font handle.
*/ */
void TagFontHandle(SCNHANDLE hf) { void SetTagFontHandle(SCNHANDLE hFont) {
hTagFont = hf; // Store the font handle hTagFont = hRegularTagFont = hFont; // Store the font handle
} }
/** /**
* Called from dec_talkfont() Glitter function. * Called from dec_talkfont() Glitter function.
* Store the talk font handle. * Store the talk font handle.
*/ */
void TalkFontHandle(SCNHANDLE hf) { void SetTalkFontHandle(SCNHANDLE hFont) {
hTalkFont = hf; // Store the font handle hTalkFont = hRegularTalkFont = hFont; // Store the font handle
} }
void SetTempTagFontHandle(SCNHANDLE hFont) {
hTagFont = hFont;
}
void SetTempTalkFontHandle(SCNHANDLE hFont) {
hTalkFont = hFont;
}
void ResetFontHandles(void) {
hTagFont = hRegularTagFont;
hTalkFont = hRegularTalkFont;
}
/** /**
* Poke the background palette into character 0's images. * Poke the background palette into character 0's images.
*/ */
void fettleFontPal(SCNHANDLE fontPal) { void FettleFontPal(SCNHANDLE fontPal) {
const FONT *pFont; const FONT *pFont;
IMAGE *pImg; IMAGE *pImg;
@ -86,11 +104,23 @@ void fettleFontPal(SCNHANDLE fontPal) {
pFont = (const FONT *)LockMem(hTagFont); pFont = (const FONT *)LockMem(hTagFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0 pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
if (!TinselV2)
pImg->hImgPal = TO_LE_32(fontPal); pImg->hImgPal = TO_LE_32(fontPal);
else
pImg->hImgPal = 0;
pFont = (const FONT *)LockMem(hTalkFont); pFont = (const FONT *)LockMem(hTalkFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0 pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
if (!TinselV2)
pImg->hImgPal = TO_LE_32(fontPal); pImg->hImgPal = TO_LE_32(fontPal);
else
pImg->hImgPal = 0;
if (TinselV2 && SysVar(SV_TAGCOLOUR)) {
static COLORREF c = GetActorRGB(-1);
SetTagColorRef(c);
UpdateDACqueue(SysVar(SV_TAGCOLOUR), 1, &c);
}
} }
} // End of namespace Tinsel } // End of namespace Tinsel

View file

@ -31,17 +31,27 @@
namespace Tinsel { namespace Tinsel {
// A temporary buffer for extracting text into is defined in font.c // A temporary buffer for extracting text into is defined in font.c
// Accessed using tBufferAddr(), this is how big it is: // Accessed using TextBufferAddr(), this is how big it is:
#define TBUFSZ 512 #define TBUFSZ 512
char *tBufferAddr(void); char *TextBufferAddr(void);
SCNHANDLE hTagFontHandle(void);
SCNHANDLE hTalkFontHandle(void);
void TagFontHandle(SCNHANDLE hf); SCNHANDLE GetTagFontHandle(void);
void TalkFontHandle(SCNHANDLE hf);
void fettleFontPal(SCNHANDLE fontPal); SCNHANDLE GetTalkFontHandle(void);
void SetTagFontHandle(SCNHANDLE hFont);
void SetTalkFontHandle(SCNHANDLE hFont);
void SetTempTagFontHandle(SCNHANDLE hFont);
void SetTempTalkFontHandle(SCNHANDLE hFont);
void ResetFontHandles(void);
void FettleFontPal(SCNHANDLE fontPal);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -28,6 +28,7 @@
#include "tinsel/handle.h" // LockMem() #include "tinsel/handle.h" // LockMem()
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/palette.h" #include "tinsel/palette.h"
#include "tinsel/scene.h"
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -46,7 +47,6 @@ extern uint8 transPalette[MAX_COLOURS];
/** /**
* Straight rendering with transparency support * Straight rendering with transparency support
*/ */
static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) { static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
// Set up the offset between destination blocks // Set up the offset between destination blocks
int rightClip = applyClipping ? pObj->rightClip : 0; int rightClip = applyClipping ? pObj->rightClip : 0;
@ -155,10 +155,91 @@ static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyCl
} }
} }
/**
* Tinsel 2 Straight rendering with transparency support
*/
static void t2WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping, bool horizFlipped) {
// Setup for correct clipping of object edges
int yClip = applyClipping ? pObj->topClip : 0;
if (applyClipping)
pObj->height -= pObj->botClip;
int numBytes;
int clipAmount;
for (int y = 0; y < pObj->height; ++y) {
// Get the position to start writing out from
uint8 *tempP = !horizFlipped ? destP :
destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
int leftClip = applyClipping ? pObj->leftClip : 0;
int rightClip = applyClipping ? pObj->rightClip : 0;
if (horizFlipped)
SWAP(leftClip, rightClip);
int x = 0;
while (x < pObj->width) {
// Get the next opcode
numBytes = *srcP++;
if (numBytes & 0x80) {
// Run length following
numBytes &= 0x7f;
clipAmount = MIN(numBytes, leftClip);
leftClip -= clipAmount;
x+= clipAmount;
int runLength = numBytes - clipAmount;
uint8 colour = *srcP++;
if ((yClip == 0) && (runLength > 0) && (colour != 0)) {
runLength = MIN(runLength, pObj->width - rightClip - x);
if (runLength > 0) {
// Non-transparent run length
colour += pObj->constant;
if (horizFlipped)
Common::set_to(tempP - runLength + 1, tempP + 1, colour);
else
Common::set_to(tempP, tempP + runLength, colour);
}
}
if (horizFlipped)
tempP -= runLength;
else
tempP += runLength;
x += numBytes - clipAmount;
} else {
// Dump a length of pixels
clipAmount = MIN(numBytes, leftClip);
leftClip -= clipAmount;
srcP += clipAmount;
int runLength = numBytes - clipAmount;
x += numBytes - runLength;
for (int xp = 0; xp < runLength; ++xp) {
if ((yClip > 0) || (x >= (pObj->width - rightClip)))
++srcP;
else if (horizFlipped)
*tempP-- = pObj->constant + *srcP++;
else
*tempP++ = pObj->constant + *srcP++;
++x;
}
}
}
assert(x == pObj->width);
if (yClip > 0)
--yClip;
else
destP += SCREEN_WIDTH;
}
}
/** /**
* Fill the destination area with a constant colour * Fill the destination area with a constant colour
*/ */
static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) { static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) { if (applyClipping) {
pObj->height -= pObj->topClip + pObj->botClip; pObj->height -= pObj->topClip + pObj->botClip;
@ -181,7 +262,6 @@ static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
* Translates the destination surface within the object's bounds using the transparency * Translates the destination surface within the object's bounds using the transparency
* lookup table from transpal.cpp (the contents of which have been moved into palette.cpp) * lookup table from transpal.cpp (the contents of which have been moved into palette.cpp)
*/ */
static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) { static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) { if (applyClipping) {
pObj->height -= pObj->topClip + pObj->botClip; pObj->height -= pObj->topClip + pObj->botClip;
@ -204,165 +284,135 @@ static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
} }
} }
#if 0
// This commented out code is the untested original WrtNonZero/ClpWrtNonZero combo method
// from the v1 source. It may be needed to be included later on to support v1 gfx files
/** /**
* Straight rendering with transparency support * Copies an uncompressed block of data straight to the screen
* Possibly only used in the Discworld Demo
*/ */
static void WrtAll(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
int objWidth = pObj->width;
static void DemoWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) { if (applyClipping) {
// FIXME: If this method is used for the demo, it still needs to be made Endian safe srcP += (pObj->topClip * pObj->width) + pObj->leftClip;
// Set up the offset between destination lines pObj->height -= pObj->topClip + pObj->botClip;
pObj->lineoffset = SCREEN_WIDTH - pObj->width - (applyClipping ? pObj->leftClip - pObj->rightClip : 0); pObj->width -= pObj->leftClip + pObj->rightClip;
// Top clipped line handling if (pObj->width <= 0)
while (applyClipping && (pObj->topClip > 0)) { return;
// Loop through discarding the data for the line
int width = pObj->width;
while (width > 0) {
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
srcP += sizeof(uint32);
if (opcodeOrLen >= 0) {
// Dump the data
srcP += ((opcodeOrLen + 3) / 4) * 4;
width -= opcodeOrLen;
} else {
// Dump the run-length opcode
width -= -opcodeOrLen;
}
} }
--pObj->height; for (int y = 0; y < pObj->height; ++y) {
--pObj->topClip; Common::copy(srcP, srcP + pObj->width, destP);
} srcP += objWidth;
destP += SCREEN_WIDTH;
// Loop for the required number of rows }
while (pObj->height > 0) { }
int width = pObj->width; /**
* Renders a packed data stream with a variable sized palette
// Handling for left edge clipping - this basically involves dumping data until we reach */
// the part of the line to be displayed static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
int clipLeft = pObj->leftClip; bool applyClipping, bool horizFlipped, int packingType) {
while (applyClipping && (clipLeft > 0)) { uint8 numColours = 0;
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP); uint8 *colourTable = NULL;
srcP += sizeof(uint32); int topClip = 0;
int xOffset = 0;
if (opcodeOrLen >= 0) { int numBytes, colour;
// Copy a specified number of bytes int v;
// Make adjustments for past the clipping width
int remainder = 4 - (opcodeOrLen % 4); if (applyClipping) {
srcP += MIN(clipLeft, opcodeOrLen); pObj->height -= pObj->botClip;
opcodeOrLen -= MIN(clipLeft, opcodeOrLen); topClip = pObj->topClip;
clipLeft -= MIN(clipLeft, opcodeOrLen); }
width -= opcodeOrLen;
if (packingType == 3) {
// Variable colours
// Handle any right edge clipping (if run length covers entire width) numColours = *srcP++;
if (width < pObj->rightClip) { colourTable = srcP;
remainder += (pObj->rightClip - width); srcP += numColours;
opcodeOrLen -= (pObj->rightClip - width); }
}
for (int y = 0; y < pObj->height; ++y) {
if (opcodeOrLen > 0) // Get the position to start writing out from
Common::copy(srcP, srcP + opcodeOrLen, destP); uint8 *tempP = !horizFlipped ? destP :
destP + (pObj->width - pObj->leftClip - pObj->rightClip) - 1;
} else { int leftClip = applyClipping ? pObj->leftClip : 0;
// Output a run length number of bytes int rightClip = applyClipping ? pObj->rightClip : 0;
// Get data for byte value and run length if (horizFlipped)
opcodeOrLen = -opcodeOrLen; SWAP(leftClip, rightClip);
int runLength = opcodeOrLen & 0xff; bool eolFlag = false;
uint8 colourVal = (opcodeOrLen >> 8) & 0xff;
// Get offset for first pixels in next line
// Make adjustments for past the clipping width xOffset = *srcP++;
runLength -= MIN(clipLeft, runLength);
clipLeft -= MIN(clipLeft, runLength); int x = 0;
width -= runLength; while (x < pObj->width) {
// Get next run size and colour to use
// Handle any right edge clipping (if run length covers entire width) for (;;) {
if (width < pObj->rightClip) if (xOffset > 0) {
runLength -= (pObj->rightClip - width); x += xOffset;
if (runLength > 0) { // Reduce offset amount by any remaining left clipping
// Displayable part starts partway through the slice v = MIN(xOffset, leftClip);
if (colourVal != 0) xOffset -= v;
Common::set_to(destP, destP + runLength, colourVal); leftClip -= v;
destP += runLength;
} if (horizFlipped) tempP -= xOffset; else tempP += xOffset;
} xOffset = 0;
}
if (width < pObj->rightClip)
width = 0; v = *srcP++;
} numBytes = v & 0xf; // No. bytes 1-15
if (packingType == 3)
// Handling for the visible part of the line colour = colourTable[v >> 4];
int endWidth = applyClipping ? pObj->rightClip : 0; else
while (width > endWidth) { colour = pObj->baseCol + (v >> 4);
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
srcP += sizeof(uint32); if (numBytes != 0)
break;
if (opcodeOrLen >= 0) {
// Copy the specified number of bytes numBytes = *srcP++;
int remainder = 4 - (opcodeOrLen % 4); if (numBytes >= 16)
break;
if (width < endWidth) {
// Shorten run length by right clipping xOffset = numBytes + v;
remainder += (pObj->rightClip - width); if (xOffset == 0) {
opcodeOrLen -= (pObj->rightClip - width); // End of line encountered
} eolFlag = true;
break;
Common::copy(srcP, srcP + opcodeOrLen, destP); }
srcP += opcodeOrLen + remainder; }
destP += opcodeOrLen;
width -= opcodeOrLen; if (eolFlag)
break;
} else {
// Handle a given run length // Apply clipping on byte sequence
opcodeOrLen = -opcodeOrLen; v = MIN(numBytes, leftClip);
int runLength = opcodeOrLen & 0xff; leftClip -= v;
uint8 colourVal = (opcodeOrLen >> 8) & 0xff; numBytes -= v;
x += v;
if (width < endWidth)
// Shorten run length by right clipping while (numBytes-- > 0) {
runLength -= (pObj->rightClip - width); if ((topClip == 0) && (x < (pObj->width - rightClip))) {
*tempP = colour;
// Only set pixels if colourVal non-zero (0 signifies transparency) if (horizFlipped) --tempP; else ++tempP;
if (colourVal != 0) }
// Fill out a run length of a specified colour ++x;
Common::set_to(destP, destP + runLength, colourVal); }
}
destP += runLength; assert(x <= pObj->width);
width -= runLength;
} if (!eolFlag) {
} // Assert that the next bytes signal a line end
assert((*srcP++ & 0xf) == 0);
// If right edge clipping is being applied, then width may still be non-zero - in assert(*srcP++ == 0);
// that case all remaining line data until the end of the line must be ignored }
while (width > 0) {
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP); if (topClip > 0)
srcP += sizeof(uint32); --topClip;
else
if (opcodeOrLen >= 0) { destP += SCREEN_WIDTH;
// Dump the data
srcP += ((opcodeOrLen + 3) / 4) * 4;
width -= opcodeOrLen;
} else {
// Dump the run-length opcode
width -= -opcodeOrLen;
}
}
--pObj->height;
destP += pObj->lineoffset;
} }
} }
#endif
//----------------- MAIN FUNCTIONS --------------------- //----------------- MAIN FUNCTIONS ---------------------
@ -380,8 +430,10 @@ void ClearScreen() {
* Updates the screen surface within the following rectangle * Updates the screen surface within the following rectangle
*/ */
void UpdateScreenRect(const Common::Rect &pClip) { void UpdateScreenRect(const Common::Rect &pClip) {
byte *pDest = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top); int yOffset = (g_system->getHeight() - SCREEN_HEIGHT) / 2;
g_system->copyRectToScreen(pDest, _vm->screen().pitch, pClip.left, pClip.top, pClip.width(), pClip.height()); byte *pSrc = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top);
g_system->copyRectToScreen(pSrc, _vm->screen().pitch, pClip.left, pClip.top + yOffset,
pClip.width(), pClip.height());
g_system->updateScreen(); g_system->updateScreen();
} }
@ -398,18 +450,76 @@ void DrawObject(DRAWOBJECT *pObj) {
// If writing constant data, don't bother locking the data pointer and reading src details // If writing constant data, don't bother locking the data pointer and reading src details
if ((pObj->flags & DMA_CONST) == 0) { if ((pObj->flags & DMA_CONST) == 0) {
byte *p = (byte *)LockMem(pObj->hBits & 0xFF800000); if (TinselV2) {
srcPtr = (byte *)LockMem(pObj->hBits);
pObj->charBase = NULL;
pObj->transOffset = 0;
} else {
byte *p = (byte *)LockMem(pObj->hBits & HANDLEMASK);
srcPtr = p + (pObj->hBits & 0x7FFFFF); srcPtr = p + (pObj->hBits & OFFSETMASK);
pObj->charBase = (char *)p + READ_LE_UINT32(p + 0x10); pObj->charBase = (char *)p + READ_LE_UINT32(p + 0x10);
pObj->transOffset = READ_LE_UINT32(p + 0x14); pObj->transOffset = READ_LE_UINT32(p + 0x14);
} }
}
// Get destination starting point // Get destination starting point
destPtr = (byte *)_vm->screen().getBasePtr(pObj->xPos, pObj->yPos); destPtr = (byte *)_vm->screen().getBasePtr(pObj->xPos, pObj->yPos);
// Handle various draw types // Handle various draw types
uint8 typeId = pObj->flags & 0xff; uint8 typeId = pObj->flags & 0xff;
if (TinselV2) {
// Tinsel v2 decoders
// Initial switch statement for the different bit packing types
int packType = pObj->flags >> 14;
if (packType == 0) {
// No colour packing
switch (typeId) {
case 0x01:
case 0x11:
case 0x41:
case 0x51:
case 0x81:
case 0xC1:
t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, (typeId & 0x10) != 0);
break;
case 0x02:
case 0x42:
// This renderer called 'RlWrtAll', but is the same as t2WrtNonZero
t2WrtNonZero(pObj, srcPtr, destPtr, typeId >= 0x40, false);
break;
case 0x04:
case 0x44:
// WrtConst with/without clipping
WrtConst(pObj, destPtr, typeId == 0x44);
break;
case 0x08:
case 0x48:
WrtAll(pObj, srcPtr, destPtr, typeId >= 0x40);
break;
case 0x84:
case 0xC4:
// WrtTrans with/without clipping
WrtTrans(pObj, destPtr, typeId == 0xC4);
break;
default:
error("Unknown drawing type %d", typeId);
}
} else {
// 1 = 16 from 240
// 2 = 16 from 224
// 3 = variable colour
if (packType == 1) pObj->baseCol = 0xF0;
else if (packType == 2) pObj->baseCol = 0xE0;
PackedWrtNonZero(pObj, srcPtr, destPtr, (pObj->flags & DMA_CLIP) != 0,
(pObj->flags & DMA_FLIPH), packType);
}
} else {
// Tinsel v1 decoders
switch (typeId) { switch (typeId) {
case 0x01: case 0x01:
case 0x08: case 0x08:
@ -420,7 +530,7 @@ void DrawObject(DRAWOBJECT *pObj) {
case 0x04: case 0x04:
case 0x44: case 0x44:
// ClpWrtConst with/without clipping // WrtConst with/without clipping
WrtConst(pObj,destPtr, typeId == 0x44); WrtConst(pObj,destPtr, typeId == 0x44);
break; break;
@ -435,6 +545,7 @@ void DrawObject(DRAWOBJECT *pObj) {
error("Unknown drawing type %d", typeId); error("Unknown drawing type %d", typeId);
break; break;
} }
}
} }
} // End of namespace Tinsel } // End of namespace Tinsel

View file

@ -37,12 +37,6 @@ namespace Tinsel {
struct PALQ; struct PALQ;
#define SCREEN_WIDTH 320 // PC screen dimensions
#define SCREEN_HEIGHT 200
#define SCRN_CENTRE_X ((SCREEN_WIDTH - 1) / 2) // screen centre x
#define SCRN_CENTRE_Y ((SCREEN_HEIGHT - 1) / 2) // screen centre y
/** draw object structure - only used when drawing objects */ /** draw object structure - only used when drawing objects */
struct DRAWOBJECT { struct DRAWOBJECT {
char *charBase; // character set base address char *charBase; // character set base address
@ -60,6 +54,7 @@ struct DRAWOBJECT {
int botClip; // amount to clip off object bottom int botClip; // amount to clip off object bottom
short xPos; // x position of object short xPos; // x position of object
short yPos; // y position of object short yPos; // y position of object
uint32 baseCol; // For 4-bit stuff
}; };

View file

@ -28,6 +28,7 @@
#include "common/file.h" #include "common/file.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/scn.h" // name of "index" file #include "tinsel/scn.h" // name of "index" file
#include "tinsel/handle.h" #include "tinsel/handle.h"
@ -41,6 +42,8 @@
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/palette.h" #include "tinsel/palette.h"
#include "tinsel/text.h" #include "tinsel/text.h"
#include "tinsel/timers.h"
#include "tinsel/tinsel.h"
#include "tinsel/scene.h" #include "tinsel/scene.h"
namespace Tinsel { namespace Tinsel {
@ -58,6 +61,7 @@ struct MEMHANDLE {
char szName[12]; //!< 00 - file name of graphics file char szName[12]; //!< 00 - file name of graphics file
int32 filesize; //!< 12 - file size and flags int32 filesize; //!< 12 - file size and flags
MEM_NODE *pNode; //!< 16 - memory node for the graphics MEM_NODE *pNode; //!< 16 - memory node for the graphics
uint32 flags2;
}; };
@ -72,7 +76,6 @@ enum {
}; };
#define FSIZE_MASK 0x00FFFFFFL //!< mask to isolate the filesize #define FSIZE_MASK 0x00FFFFFFL //!< mask to isolate the filesize
#define MALLOC_MASK 0xFF000000L //!< mask to isolate the memory allocation flags #define MALLOC_MASK 0xFF000000L //!< mask to isolate the memory allocation flags
#define OFFSETMASK 0x007fffffL //!< get offset of address
//#define HANDLEMASK 0xFF800000L //!< get handle of address //#define HANDLEMASK 0xFF800000L //!< get handle of address
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
@ -83,6 +86,13 @@ static MEMHANDLE *handleTable = 0;
// number of handles in the handle table // number of handles in the handle table
static uint numHandles = 0; static uint numHandles = 0;
static uint32 cdPlayHandle = (uint32)-1;
static int cdPlayFileNum, cdPlaySceneNum;
static SCNHANDLE cdBaseHandle = 0, cdTopHandle = 0;
static Common::File cdGraphStream;
static char szCdPlayFile[100];
//----------------- FORWARD REFERENCES -------------------- //----------------- FORWARD REFERENCES --------------------
@ -94,7 +104,8 @@ static void LoadFile(MEMHANDLE *pH, bool bWarn); // load a memory block as a fil
* permanent graphics etc. * permanent graphics etc.
*/ */
void SetupHandleTable(void) { void SetupHandleTable(void) {
enum { RECORD_SIZE = 20 }; bool t2Flag = (TinselVersion == TINSEL_V2);
int RECORD_SIZE = t2Flag ? 24 : 20;
int len; int len;
uint i; uint i;
@ -108,7 +119,7 @@ void SetupHandleTable(void) {
if (len > 0) { if (len > 0) {
if ((len % RECORD_SIZE) != 0) { if ((len % RECORD_SIZE) != 0) {
// index file is corrupt // index file is corrupt
error("File %s is corrupt", INDEX_FILENAME); error(FILE_IS_CORRUPT, INDEX_FILENAME);
} }
// calc number of handles // calc number of handles
@ -128,31 +139,36 @@ void SetupHandleTable(void) {
// need to read that from the file. // need to read that from the file.
handleTable[i].pNode = NULL; handleTable[i].pNode = NULL;
f.seek(4, SEEK_CUR); f.seek(4, SEEK_CUR);
// For Discworld 2, read in the flags2 field
handleTable[i].flags2 = t2Flag ? f.readUint32LE() : 0;
} }
if (f.ioFailed()) { if (f.ioFailed()) {
// index file is corrupt // index file is corrupt
error("File %s is corrupt", INDEX_FILENAME); error(FILE_IS_CORRUPT, INDEX_FILENAME);
} }
// close the file // close the file
f.close(); f.close();
} else { // index file is corrupt } else { // index file is corrupt
error("File %s is corrupt", INDEX_FILENAME); error(FILE_IS_CORRUPT, INDEX_FILENAME);
} }
} else { // cannot find the index file } else { // cannot find the index file
error("Cannot find file %s", INDEX_FILENAME); error(CANNOT_FIND_FILE, INDEX_FILENAME);
} }
// allocate memory nodes and load all permanent graphics // allocate memory nodes and load all permanent graphics
for (i = 0, pH = handleTable; i < numHandles; i++, pH++) { for (i = 0, pH = handleTable; i < numHandles; i++, pH++) {
if (pH->filesize & fPreload) { if (pH->filesize & fPreload) {
// allocate a fixed memory node for permanent files // allocate a fixed memory node for permanent files
pH->pNode = MemoryAlloc(DWM_FIXED, pH->filesize & FSIZE_MASK); pH->pNode = MemoryAlloc(DWM_FIXED, sizeof(MEM_NODE) + (pH->filesize & FSIZE_MASK));
// make sure memory allocated // make sure memory allocated
assert(pH->pNode); assert(pH->pNode);
// Initialise the MEM_NODE structure
memset(pH->pNode, 0, sizeof(MEM_NODE));
// load the data // load the data
LoadFile(pH, true); LoadFile(pH, true);
} }
@ -178,8 +194,101 @@ void FreeHandleTable(void) {
free(handleTable); free(handleTable);
handleTable = NULL; handleTable = NULL;
} }
if (cdGraphStream.isOpen())
cdGraphStream.close();
} }
/**
* Loads a memory block as a file.
*/
void OpenCDGraphFile(void) {
if (cdGraphStream.isOpen())
cdGraphStream.close();
// As the theory goes, the right CD will be in there!
cdGraphStream.clearIOFailed();
cdGraphStream.open(szCdPlayFile);
if (cdGraphStream.ioFailed())
error(CANNOT_FIND_FILE, szCdPlayFile);
}
void LoadCDGraphData(MEMHANDLE *pH) {
// read the data
uint bytes;
byte *addr;
int retries = 0;
assert(!(pH->filesize & fCompressed));
// Can't be preloaded
assert(!(pH->filesize & fPreload));
// discardable - lock the memory
addr = (byte *)MemoryLock(pH->pNode);
// make sure address is valid
assert(addr);
// Move to correct place in file and load the required data
cdGraphStream.seek(cdBaseHandle & OFFSETMASK, SEEK_SET);
bytes = cdGraphStream.read(addr, (cdTopHandle - cdBaseHandle) & OFFSETMASK);
// New code to try and handle CD read failures 24/2/97
while (bytes != ((cdTopHandle - cdBaseHandle) & OFFSETMASK) && retries++ < MAX_READ_RETRIES) {
// Try again
cdGraphStream.seek(cdBaseHandle & OFFSETMASK, SEEK_SET);
bytes = cdGraphStream.read(addr, (cdTopHandle - cdBaseHandle) & OFFSETMASK);
}
// discardable - unlock the memory
MemoryUnlock(pH->pNode);
// set the loaded flag
pH->filesize |= fLoaded;
// clear the loading flag
// pH->filesize &= ~fLoading;
if (bytes != ((cdTopHandle-cdBaseHandle) & OFFSETMASK))
// file is corrupt
error(FILE_READ_ERROR, "CD play file");
}
/**
* Called immediatly preceding a CDplay().
* Prepares the ground so that when LockMem() is called, the
* appropriate section of the extra scene file is loaded.
* @param start Handle of start of range
* @param next Handle of end of range + 1
*/
void LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) {
if (cdPlayFileNum == cdPlaySceneNum && start == cdBaseHandle)
return;
OpenCDGraphFile();
if ((handleTable + cdPlayHandle)->pNode->pBaseAddr != NULL)
MemoryDiscard((handleTable + cdPlayHandle)->pNode); // Free it
// It must always be the same
assert(cdPlayHandle == (start >> SCNHANDLE_SHIFT));
assert(cdPlayHandle == (next >> SCNHANDLE_SHIFT));
cdBaseHandle = start;
cdTopHandle = next;
}
void SetCdPlaySceneDetails(int fileNum, const char *fileName) {
cdPlaySceneNum = fileNum;
strcpy(szCdPlayFile, fileName);
}
void SetCdPlayHandle(int fileNum) {
cdPlayHandle = fileNum;
}
/** /**
* Loads a memory block as a file. * Loads a memory block as a file.
* @param pH Memory block pointer * @param pH Memory block pointer
@ -204,7 +313,7 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (pH->filesize & fPreload) if (pH->filesize & fPreload)
// preload - no need to lock the memory // preload - no need to lock the memory
addr = (uint8 *)pH->pNode; addr = (uint8 *)pH->pNode + sizeof(MEM_NODE);
else { else {
// discardable - lock the memory // discardable - lock the memory
addr = (uint8 *)MemoryLock(pH->pNode); addr = (uint8 *)MemoryLock(pH->pNode);
@ -243,12 +352,12 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (bWarn) if (bWarn)
// file is corrupt // file is corrupt
error("File %s is corrupt", szFilename); error(FILE_IS_CORRUPT, szFilename);
} }
if (bWarn) if (bWarn)
// cannot find file // cannot find file
error("Cannot find file %s", szFilename); error(CANNOT_FIND_FILE, szFilename);
} }
/** /**
@ -265,8 +374,39 @@ uint8 *LockMem(SCNHANDLE offset) {
pH = handleTable + handle; pH = handleTable + handle;
if (pH->filesize & fPreload) { if (pH->filesize & fPreload) {
if (TinselV2)
// update the LRU time (new in this file)
pH->pNode->lruTime = DwGetCurrentTime();
// permanent files are already loaded // permanent files are already loaded
return (uint8 *)pH->pNode + (offset & OFFSETMASK); return (uint8 *)pH->pNode + sizeof(MEM_NODE) + (offset & OFFSETMASK);
} else if (handle == cdPlayHandle) {
// Must be in currently loaded/loadable range
if(offset < cdBaseHandle || offset >= cdTopHandle)
error("Overlapping (in time) CD-plays!");
if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded))
// already allocated and loaded
return pH->pNode->pBaseAddr + ((offset - cdBaseHandle) & OFFSETMASK);
if (pH->pNode->pBaseAddr == NULL)
// must have been discarded - reallocate the memory
MemoryReAlloc(pH->pNode, cdTopHandle-cdBaseHandle,
DWM_MOVEABLE | DWM_DISCARDABLE);
if(pH->pNode->pBaseAddr == NULL)
error("Out of memory");
LoadCDGraphData(pH);
// make sure address is valid
assert(pH->pNode->pBaseAddr);
// update the LRU time (new in this file)
pH->pNode->lruTime = DwGetCurrentTime();
return pH->pNode->pBaseAddr + ((offset - cdBaseHandle) & OFFSETMASK);
} else { } else {
if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded)) if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded))
// already allocated and loaded // already allocated and loaded
@ -312,7 +452,10 @@ void LockScene(SCNHANDLE offset) {
if ((pH->filesize & fPreload) == 0) { if ((pH->filesize & fPreload) == 0) {
// change the flags for the node // change the flags for the node
MemoryReAlloc(pH->pNode, pH->filesize & FSIZE_MASK, DWM_MOVEABLE); // WORKAROUND: The original didn't include the DWM_LOCKED flag. It's being
// included because the method is 'LockScene' so it's presumed that the
// point of this was that the scene's memory block be locked
MemoryReAlloc(pH->pNode, pH->filesize & FSIZE_MASK, DWM_MOVEABLE | DWM_LOCKED);
#ifdef DEBUG #ifdef DEBUG
bLockedScene = true; bLockedScene = true;
#endif #endif
@ -363,4 +506,50 @@ bool ValidHandle(SCNHANDLE offset) {
} }
#endif #endif
/**
* TouchMem
* @param offset Handle and offset to data
*/
void TouchMem(SCNHANDLE offset) {
MEMHANDLE *pH; // points to table entry
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
if (offset != 0) {
pH = handleTable + handle;
// update the LRU time whether its loaded or not!
pH->pNode->lruTime = DwGetCurrentTime();
}
}
/**
* Returns true if the given handle is into the cd graph data
* @param offset Handle and offset to data
*/
bool IsCdPlayHandle(SCNHANDLE offset) {
uint32 handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
// range check the memory handle
assert(handle < numHandles);
return (handle == cdPlayHandle);
}
/**
* Returns the CD for a given scene handle
*/
int CdNumber(SCNHANDLE offset) {
uint handle = offset >> SCNHANDLE_SHIFT; // calc memory handle to use
// range check the memory handle
assert(handle < numHandles);
MEMHANDLE *pH = handleTable + handle;
if (!TinselV2)
return 1;
return GetCD(pH->flags2 & fAllCds);
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -48,6 +48,23 @@ void LockScene( // Called to make the current scene non-discardable
void UnlockScene( // Called to make the current scene discardable again void UnlockScene( // Called to make the current scene discardable again
SCNHANDLE offset); // handle and offset to data SCNHANDLE offset); // handle and offset to data
bool IsCdPlayHandle(SCNHANDLE offset);
void TouchMem(SCNHANDLE offset);
void SetCdPlaySceneDetails( // Called at scene startup
int sceneNum,
const char *fileName);
void SetCdPlayHandle( // Called at game startup
int fileNum);
void LoadExtraGraphData(
SCNHANDLE start, // Handle of start of range
SCNHANDLE next); // Handle of end of range + 1
int CdNumber(SCNHANDLE offset);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_HANDLE_H #endif // TINSEL_HANDLE_H

View file

@ -26,11 +26,13 @@
#include "tinsel/heapmem.h" #include "tinsel/heapmem.h"
#include "tinsel/timers.h" // For DwGetCurrentTime #include "tinsel/timers.h" // For DwGetCurrentTime
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
// minimum memory required for MS-DOS version of game // Specifies the total amount of memory required for DW1 demo, DW1, or DW2 respectively.
#define MIN_MEM 2506752L // Currently this is set at 10Mb for all three - this could probably be reduced somewhat
uint32 MemoryPoolSize[3] = {10 * 1024 * 1024, 10 * 1024 * 1024, 10 * 1024 * 1024};
// list of all memory nodes // list of all memory nodes
MEM_NODE mnodeList[NUM_MNODES]; MEM_NODE mnodeList[NUM_MNODES];
@ -73,8 +75,10 @@ void MemoryInit(void) {
// null the last mnode // null the last mnode
mnodeList[NUM_MNODES - 1].pNext = NULL; mnodeList[NUM_MNODES - 1].pNext = NULL;
// allocatea big chunk of memory // allocates a big chunk of memory
const uint32 size = 2*MIN_MEM+655360L; uint32 size = MemoryPoolSize[0];
if (TinselVersion == TINSEL_V1) size = MemoryPoolSize[1];
else if (TinselVersion == TINSEL_V2) size = MemoryPoolSize[2];
uint8 *mem = (uint8 *)malloc(size); uint8 *mem = (uint8 *)malloc(size);
assert(mem); assert(mem);
@ -274,8 +278,9 @@ MEM_NODE *MemoryAlloc(int flags, long size) {
bool bCompacted = true; // set when heap has been compacted bool bCompacted = true; // set when heap has been compacted
// compact the heap if we are allocating fixed memory // compact the heap if we are allocating fixed memory
if (flags & DWM_FIXED) if (flags & DWM_FIXED) {
HeapCompact(MAX_INT, false); HeapCompact(MAX_INT, false);
}
while ((flags & DWM_NOALLOC) == 0 && bCompacted) { while ((flags & DWM_NOALLOC) == 0 && bCompacted) {
// search the heap for a free block // search the heap for a free block

View file

@ -24,6 +24,7 @@
* Functions to set up moving actors' reels. * Functions to set up moving actors' reels.
*/ */
#include "tinsel/handle.h"
#include "tinsel/pcode.h" // For D_UP, D_DOWN #include "tinsel/pcode.h" // For D_UP, D_DOWN
#include "tinsel/rince.h" #include "tinsel/rince.h"
@ -34,7 +35,7 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
enum { enum {
NUM_INTERVALS = NUM_MAINSCALES - 1, NUM_INTERVALS = REQ_MAIN_SCALES - 1,
// 2 for up and down, 3 allow enough entries for 3 fully subscribed moving actors' worth // 2 for up and down, 3 allow enough entries for 3 fully subscribed moving actors' worth
MAX_SCRENTRIES = NUM_INTERVALS*2*3 MAX_SCRENTRIES = NUM_INTERVALS*2*3
@ -51,36 +52,80 @@ static SCIdataStruct SCIdata[MAX_SCRENTRIES];
static int scrEntries = 0; static int scrEntries = 0;
/**
* Sets an actor's walk reels
*/
void SetWalkReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->walkReels[scale-1][LEFTREEL] = al;
pMover->walkReels[scale-1][RIGHTREEL] = ar;
pMover->walkReels[scale-1][FORWARD] = af;
pMover->walkReels[scale-1][AWAY] = aa;
}
/**
* Sets an actor's stand reels
*/
void SetStandReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->standReels[scale-1][LEFTREEL] = al;
pMover->standReels[scale-1][RIGHTREEL] = ar;
pMover->standReels[scale-1][FORWARD] = af;
pMover->standReels[scale-1][AWAY] = aa;
}
/**
* Sets an actor's talk reels
*/
void SetTalkReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa) {
assert(scale > 0 && scale <= TOTAL_SCALES);
pMover->talkReels[scale-1][LEFTREEL] = al;
pMover->talkReels[scale-1][RIGHTREEL] = ar;
pMover->talkReels[scale-1][FORWARD] = af;
pMover->talkReels[scale-1][AWAY] = aa;
}
/** /**
* Return handle to actor's talk reel at present scale and direction. * Return handle to actor's talk reel at present scale and direction.
*/ */
SCNHANDLE GetMactorTalkReel(PMACTOR pActor, TFTYPE dirn) { SCNHANDLE GetMoverTalkReel(PMOVER pActor, TFTYPE dirn) {
assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES); assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES);
switch (dirn) { switch (dirn) {
case TF_NONE: case TF_NONE:
return pActor->TalkReels[pActor->scale-1][pActor->dirn]; return pActor->talkReels[pActor->scale-1][pActor->direction];
case TF_UP: case TF_UP:
return pActor->TalkReels[pActor->scale-1][AWAY]; return pActor->talkReels[pActor->scale-1][AWAY];
case TF_DOWN: case TF_DOWN:
return pActor->TalkReels[pActor->scale-1][FORWARD]; return pActor->talkReels[pActor->scale-1][FORWARD];
case TF_LEFT: case TF_LEFT:
return pActor->TalkReels[pActor->scale-1][LEFTREEL]; return pActor->talkReels[pActor->scale-1][LEFTREEL];
case TF_RIGHT: case TF_RIGHT:
return pActor->TalkReels[pActor->scale-1][RIGHTREEL]; return pActor->talkReels[pActor->scale-1][RIGHTREEL];
default: default:
error("GetMactorTalkReel() - illegal direction!"); error("GetMoverTalkReel() - illegal direction!");
} }
} }
/** /**
* scalingreels * scalingreels
*/ */
void setscalingreels(int actor, int scale, int direction, void SetScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) { SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away) {
assert(scale >= 1 && scale <= NUM_MAINSCALES); // invalid scale assert(scale >= 1 && scale <= NUM_MAINSCALES); // invalid scale
assert(!(scale == 1 && direction == D_UP) && assert(!(scale == 1 && direction == D_UP) &&
@ -101,7 +146,7 @@ void setscalingreels(int actor, int scale, int direction,
/** /**
* ScalingReel * ScalingReel
*/ */
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRREEL reel) { SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel) {
int d; // Direction int d; // Direction
// The smaller the number, the bigger the scale // The smaller the number, the bigger the scale
@ -129,4 +174,20 @@ void RebootScalingReels(void) {
memset(SCIdata, 0, sizeof(SCIdata)); memset(SCIdata, 0, sizeof(SCIdata));
} }
/**
* Discourage them from being ditched.
*/
void TouchMoverReels(void) {
PMOVER pMover;
int scale;
pMover = NextMover(NULL);
do {
for (scale = 0; scale < TOTAL_SCALES; scale++) {
TouchMem(pMover->walkReels[scale][LEFTREEL]);
}
} while ((pMover = NextMover(pMover)) != NULL);
}
} // end of namespace Tinsel } // end of namespace Tinsel

56
engines/tinsel/mareels.h Normal file
View file

@ -0,0 +1,56 @@
/* 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$
*
*/
#ifndef TINSEL_MAREELS_H // prevent multiple includes
#define TINSEL_MAREELS_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/rince.h"
namespace Tinsel {
void SetWalkReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
void SetStandReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
void SetTalkReels(PMOVER pMover, int scale,
SCNHANDLE al, SCNHANDLE ar, SCNHANDLE af, SCNHANDLE aa);
SCNHANDLE GetMoverTalkReel(PMOVER pActor, TFTYPE dirn);
void SetScalingReels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away);
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel);
void RebootScalingReels(void);
void TouchMoverReels(void);
} // end of namespace Tinsel
#endif

View file

@ -5,11 +5,14 @@ MODULE_OBJS = \
anim.o \ anim.o \
background.o \ background.o \
bg.o \ bg.o \
bmv.o \
cliprect.o \ cliprect.o \
config.o \ config.o \
cursor.o \ cursor.o \
debugger.o \ debugger.o \
detection.o \ detection.o \
dialogs.o \
drives.o \
effect.o \ effect.o \
events.o \ events.o \
faders.o \ faders.o \
@ -17,7 +20,6 @@ MODULE_OBJS = \
graphics.o \ graphics.o \
handle.o \ handle.o \
heapmem.o \ heapmem.o \
inventory.o \
mareels.o \ mareels.o \
move.o \ move.o \
multiobj.o \ multiobj.o \
@ -37,6 +39,7 @@ MODULE_OBJS = \
scroll.o \ scroll.o \
sound.o \ sound.o \
strres.o \ strres.o \
sysvar.o \
text.o \ text.o \
timers.o \ timers.o \
tinlib.o \ tinlib.o \

File diff suppressed because it is too large Load diff

View file

@ -27,17 +27,24 @@
#define TINSEL_MOVE_H #define TINSEL_MOVE_H
#include "tinsel/dw.h" // for SCNHANDLE #include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/rince.h"
namespace Tinsel { namespace Tinsel {
struct MACTOR; struct MOVER;
void SetActorDest(MACTOR *pActor, int x, int y, bool igPath, SCNHANDLE film); typedef enum { YB_X2, YB_X1_5 } YBIAS;
void SSetActorDest(MACTOR *pActor);
void DoMoveActor(MACTOR *pActor); int SetActorDest(MOVER *pMover, int x, int y, bool igPath, SCNHANDLE film);
void SSetActorDest(MOVER *pActor);
void DoMoveActor(MOVER *pMover);
void SetDefaultRefer(int32 defRefer); void SetDefaultRefer(int32 defRefer);
int GetLastLeadXdest(void);
int GetLastLeadYdest(void);
DIRECTION GetDirection(int fromx, int fromy, int tox, int toy, DIRECTION lastreel,
HPOLYGON hPath, YBIAS yBias = YB_X2);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif /* TINSEL_MOVE_H */ #endif /* TINSEL_MOVE_H */

View file

@ -27,6 +27,7 @@
#include "tinsel/multiobj.h" #include "tinsel/multiobj.h"
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -204,6 +205,8 @@ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
if (deltaX == 0 && deltaY == 0) if (deltaX == 0 && deltaY == 0)
return; // ignore no change return; // ignore no change
if (!TinselV2) {
// *** This may be wrong!!!
if (pMultiObj->flags & DMA_FLIPH) { if (pMultiObj->flags & DMA_FLIPH) {
// image is flipped horizontally - flip the x direction // image is flipped horizontally - flip the x direction
deltaX = -deltaX; deltaX = -deltaX;
@ -213,6 +216,7 @@ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
// image is flipped vertically - flip the y direction // image is flipped vertically - flip the y direction
deltaY = -deltaY; deltaY = -deltaY;
} }
}
// for all the objects that make up this multi-part // for all the objects that make up this multi-part
do { do {
@ -530,4 +534,32 @@ int MultiLowest(OBJECT *pMulti) {
return lowest - 1; return lowest - 1;
} }
/**
* Returns TRUE if the object currently has an image.
* @param pMulti Multi-part object
*/
bool MultiHasShape(POBJECT pMulti) {
return (pMulti->hShape != 0);
}
/**
* Bodge for text on movies. Makes sure it appears for it's lifetime.
* @param pMultiObj Multi-part object to be adjusted
*/
void MultiForceRedraw(POBJECT pMultiObj) {
// validate object pointer
assert(pMultiObj >= objectList && pMultiObj <= objectList + NUM_OBJECTS - 1);
// for all the objects that make up this multi-part
do {
// signal a change in the object
pMultiObj->flags |= DMA_CHANGED;
// next obj in list
pMultiObj = pMultiObj->pSlave;
} while (pMultiObj != NULL);
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -28,6 +28,7 @@
#define TINSEL_MULTIOBJ_H #define TINSEL_MULTIOBJ_H
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/object.h"
namespace Tinsel { namespace Tinsel {
@ -45,7 +46,9 @@ struct MULTI_INIT {
int32 mulX; //!< multi-objects initial x ani position int32 mulX; //!< multi-objects initial x ani position
int32 mulY; //!< multi-objects initial y ani position int32 mulY; //!< multi-objects initial y ani position
int32 mulZ; //!< multi-objects initial z position int32 mulZ; //!< multi-objects initial z position
uint32 otherFlags; //!< multi-objects Tinsel 2 - other flags
} PACKED_STRUCT; } PACKED_STRUCT;
typedef MULTI_INIT *PMULTI_INIT;
#include "common/pack-end.h" // END STRUCT PACKING #include "common/pack-end.h" // END STRUCT PACKING
@ -119,6 +122,12 @@ int MultiHighest( // Returns the highest point of a multi-part object
int MultiLowest( // Returns the lowest point of a multi-part object int MultiLowest( // Returns the lowest point of a multi-part object
OBJECT *pMulti); // multi-part object OBJECT *pMulti); // multi-part object
bool MultiHasShape( // Returns TRUE if the object currently has an image
POBJECT pMulti); // multi-part object
void MultiForceRedraw(
POBJECT pMultiObj); // multi-part object to be forced
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_MULTIOBJ_H #endif // TINSEL_MULTIOBJ_H

View file

@ -31,12 +31,21 @@
#include "sound/mididrv.h" #include "sound/mididrv.h"
#include "sound/midiparser.h" #include "sound/midiparser.h"
#include "sound/audiocd.h" #include "sound/audiocd.h"
#include "sound/adpcm.h"
#include "common/config-manager.h" #include "common/config-manager.h"
#include "common/file.h" #include "common/file.h"
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"
#include "tinsel/music.h" #include "tinsel/music.h"
#include "tinsel/handle.h"
#include "tinsel/sysvar.h"
#define MUSIC_JUMP (-1)
#define MUSIC_END (-2)
#define BLMAGIC (-3458)
#define DIM_SPEED 8
namespace Tinsel { namespace Tinsel {
@ -153,8 +162,8 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
currentMidi = dwFileOffset; currentMidi = dwFileOffset;
currentLoop = bLoop; currentLoop = bLoop;
if (volMidi != 0) { if (volMusic != 0) {
SetMidiVolume(volMidi); SetMidiVolume(volMusic);
// Support for compressed music from the music enhancement project // Support for compressed music from the music enhancement project
AudioCD.stop(); AudioCD.stop();
@ -191,7 +200,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
// open MIDI sequence file in binary mode // open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE)) if (!midiStream.open(MIDI_FILE))
error("Cannot find file %s", MIDI_FILE); error(CANNOT_FIND_FILE, MIDI_FILE);
// update index of last tune loaded // update index of last tune loaded
dwLastMidiIndex = dwMidiIndex; dwLastMidiIndex = dwMidiIndex;
@ -206,22 +215,22 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size); assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size);
// stop any currently playing tune // stop any currently playing tune
_vm->_music->stop(); _vm->_midiMusic->stop();
// read the sequence // read the sequence
if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen) if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen)
error("File %s is corrupt", MIDI_FILE); error(FILE_IS_CORRUPT, MIDI_FILE);
midiStream.close(); midiStream.close();
_vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
// Store the length // Store the length
dwLastSeqLen = dwSeqLen; dwLastSeqLen = dwSeqLen;
} else { } else {
// dwMidiIndex == dwLastMidiIndex // dwMidiIndex == dwLastMidiIndex
_vm->_music->stop(); _vm->_midiMusic->stop();
_vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
} }
// allow another sequence to play // allow another sequence to play
@ -235,7 +244,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
*/ */
bool MidiPlaying(void) { bool MidiPlaying(void) {
if (AudioCD.isPlaying()) return true; if (AudioCD.isPlaying()) return true;
return _vm->_music->isPlaying(); return _vm->_midiMusic->isPlaying();
} }
/** /**
@ -246,7 +255,7 @@ bool StopMidi(void) {
currentLoop = false; currentLoop = false;
AudioCD.stop(); AudioCD.stop();
_vm->_music->stop(); _vm->_midiMusic->stop();
return true; return true;
} }
@ -255,7 +264,7 @@ bool StopMidi(void) {
* Gets the volume of the MIDI music. * Gets the volume of the MIDI music.
*/ */
int GetMidiVolume() { int GetMidiVolume() {
return volMidi; return volMusic;
} }
/** /**
@ -265,24 +274,24 @@ int GetMidiVolume() {
void SetMidiVolume(int vol) { void SetMidiVolume(int vol) {
assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume); assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
if (vol == 0 && volMidi == 0) { if (vol == 0 && volMusic == 0) {
// Nothing to do // Nothing to do
} else if (vol == 0 && volMidi != 0) { } else if (vol == 0 && volMusic != 0) {
// Stop current midi sequence // Stop current midi sequence
AudioCD.stop(); AudioCD.stop();
StopMidi(); StopMidi();
} else if (vol != 0 && volMidi == 0) { } else if (vol != 0 && volMusic == 0) {
// Perhaps restart last midi sequence // Perhaps restart last midi sequence
if (currentLoop) { if (currentLoop) {
PlayMidiSequence(currentMidi, true); PlayMidiSequence(currentMidi, true);
_vm->_music->setVolume(vol); _vm->_midiMusic->setVolume(vol);
} }
} else if (vol != 0 && volMidi != 0) { } else if (vol != 0 && volMusic != 0) {
// Alter current volume // Alter current volume
_vm->_music->setVolume(vol); _vm->_midiMusic->setVolume(vol);
} }
volMidi = vol; volMusic = vol;
} }
/** /**
@ -292,7 +301,7 @@ void OpenMidiFiles(void) {
Common::File midiStream; Common::File midiStream;
// Demo version has no midi file // Demo version has no midi file
if (_vm->getFeatures() & GF_DEMO) if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2))
return; return;
if (midiBuffer.pDat) if (midiBuffer.pDat)
@ -301,12 +310,12 @@ void OpenMidiFiles(void) {
// open MIDI sequence file in binary mode // open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE)) if (!midiStream.open(MIDI_FILE))
error("Cannot find file %s", MIDI_FILE); error(CANNOT_FIND_FILE, MIDI_FILE);
// gen length of the largest sequence // gen length of the largest sequence
midiBuffer.size = midiStream.readUint32LE(); midiBuffer.size = midiStream.readUint32LE();
if (midiStream.ioFailed()) if (midiStream.ioFailed())
error("File %s is corrupt", MIDI_FILE); error(FILE_IS_CORRUPT, MIDI_FILE);
if (midiBuffer.size) { if (midiBuffer.size) {
// allocate a buffer big enough for the largest MIDI sequence // allocate a buffer big enough for the largest MIDI sequence
@ -327,14 +336,14 @@ void DeleteMidiBuffer() {
midiBuffer.pDat = NULL; midiBuffer.pDat = NULL;
} }
MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) { MidiMusicPlayer::MidiMusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) {
memset(_channel, 0, sizeof(_channel)); memset(_channel, 0, sizeof(_channel));
_masterVolume = 0; _masterVolume = 0;
this->open(); this->open();
_xmidiParser = MidiParser::createParser_XMIDI(); _xmidiParser = MidiParser::createParser_XMIDI();
} }
MusicPlayer::~MusicPlayer() { MidiMusicPlayer::~MidiMusicPlayer() {
_driver->setTimerCallback(NULL, NULL); _driver->setTimerCallback(NULL, NULL);
stop(); stop();
this->close(); this->close();
@ -342,7 +351,7 @@ MusicPlayer::~MusicPlayer() {
delete _xmidiParser; delete _xmidiParser;
} }
void MusicPlayer::setVolume(int volume) { void MidiMusicPlayer::setVolume(int volume) {
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume);
if (_masterVolume == volume) if (_masterVolume == volume)
@ -359,7 +368,7 @@ void MusicPlayer::setVolume(int volume) {
} }
} }
int MusicPlayer::open() { int MidiMusicPlayer::open() {
// Don't ever call open without first setting the output driver! // Don't ever call open without first setting the output driver!
if (!_driver) if (!_driver)
return 255; return 255;
@ -372,14 +381,14 @@ int MusicPlayer::open() {
return 0; return 0;
} }
void MusicPlayer::close() { void MidiMusicPlayer::close() {
stop(); stop();
if (_driver) if (_driver)
_driver->close(); _driver->close();
_driver = 0; _driver = 0;
} }
void MusicPlayer::send(uint32 b) { void MidiMusicPlayer::send(uint32 b) {
byte channel = (byte)(b & 0x0F); byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) { if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume // Adjust volume changes by master volume
@ -409,7 +418,7 @@ void MusicPlayer::send(uint32 b) {
} }
} }
void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { void MidiMusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) { switch (type) {
case 0x2F: // End of Track case 0x2F: // End of Track
if (_looping) if (_looping)
@ -423,15 +432,15 @@ void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
} }
} }
void MusicPlayer::onTimer(void *refCon) { void MidiMusicPlayer::onTimer(void *refCon) {
MusicPlayer *music = (MusicPlayer *)refCon; MidiMusicPlayer *music = (MidiMusicPlayer *)refCon;
Common::StackLock lock(music->_mutex); Common::StackLock lock(music->_mutex);
if (music->_isPlaying) if (music->_isPlaying)
music->_parser->onTimer(); music->_parser->onTimer();
} }
void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
if (_isPlaying) if (_isPlaying)
return; return;
@ -463,7 +472,7 @@ void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
} }
} }
void MusicPlayer::stop() { void MidiMusicPlayer::stop() {
Common::StackLock lock(_mutex); Common::StackLock lock(_mutex);
_isPlaying = false; _isPlaying = false;
@ -473,16 +482,414 @@ void MusicPlayer::stop() {
} }
} }
void MusicPlayer::pause() { void MidiMusicPlayer::pause() {
setVolume(-1); setVolume(-1);
_isPlaying = false; _isPlaying = false;
} }
void MusicPlayer::resume() { void MidiMusicPlayer::resume() {
setVolume(GetMidiVolume()); setVolume(GetMidiVolume());
_isPlaying = true; _isPlaying = true;
} }
PCMMusicPlayer::PCMMusicPlayer() {
_silenceSamples = 0;
_curChunk = 0;
_fileName = 0;
_state = S_IDLE;
_mState = S_IDLE;
_scriptNum = -1;
_scriptIndex = 0;
_forcePlay = false;
_volume = 255;
_dimmed = false;
_dimmedTinsel = false;
_dimIteration = 0;
_fadeOutVolume = 0;
_fadeOutIteration = 0;
_end = true;
_vm->_mixer->playInputStream(Audio::Mixer::kMusicSoundType,
&_handle, this, -1, _volume, 0, false, true);
}
PCMMusicPlayer::~PCMMusicPlayer() {
_vm->_mixer->stopHandle(_handle);
delete[] _fileName;
}
void PCMMusicPlayer::startPlay(int id) {
if (!_fileName)
return;
debugC(DEBUG_DETAILED, kTinselDebugMusic, "Playing PCM music %s, index %d", _fileName, id);
Common::StackLock slock(_mutex);
stop();
_scriptNum = id;
_scriptIndex = 1;
_state = S_NEW;
play();
}
void PCMMusicPlayer::stopPlay() {
Common::StackLock slock(_mutex);
stop();
}
int PCMMusicPlayer::readBuffer(int16 *buffer, const int numSamples) {
Common::StackLock slock(_mutex);
if (!_curChunk && ((_state == S_IDLE) || (_state == S_STOP)))
return 0;
int samplesLeft = numSamples;
while (samplesLeft > 0) {
if (_silenceSamples > 0) {
int n = MIN(_silenceSamples, samplesLeft);
memset(buffer, 0, n);
buffer += n;
_silenceSamples -= n;
samplesLeft -= n;
} else if (_curChunk &&
((_state == S_MID) || (_state == S_NEXT) || (_state == S_NEW))) {
int n = _curChunk->readBuffer(buffer, samplesLeft);
buffer += n;
samplesLeft -= n;
if (_curChunk->endOfData()) {
_state = S_END1;
delete _curChunk;
_curChunk = 0;
}
} else {
if (!getNextChunk())
break;
}
}
return (numSamples - samplesLeft);
}
bool PCMMusicPlayer::isPlaying() const {
return ((_state != S_IDLE) && (_state != S_STOP));
}
bool PCMMusicPlayer::isDimmed() const {
return _dimmed;
}
void PCMMusicPlayer::getTunePlaying(void *voidPtr, int length) {
Common::StackLock lock(_mutex);
debugC(DEBUG_DETAILED, kTinselDebugMusic, "getTunePlaying");
assert(length == (3 * sizeof(int32)));
int32 *p = (int32 *) voidPtr;
_mState = _state;
p[0] = (int32) _mState;
p[1] = _scriptNum;
p[2] = _scriptIndex;
}
void PCMMusicPlayer::restoreThatTune(void *voidPtr) {
Common::StackLock lock(_mutex);
debugC(DEBUG_DETAILED, kTinselDebugMusic, "restoreThatTune");
int32 *p = (int32 *) voidPtr;
_mState = (State) p[0];
_scriptNum = p[1];
_scriptIndex = p[2];
if (_mState != S_IDLE)
_state = S_NEW;
delete _curChunk;
_curChunk = 0;
_end = false;
}
void PCMMusicPlayer::setMusicSceneDetails(SCNHANDLE hScript,
SCNHANDLE hSegment, const char *fileName) {
Common::StackLock lock(_mutex);
stop();
debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Setting music scene details: %s", fileName);
_hScript = hScript;
_hSegment = hSegment;
_fileName = new char[strlen(fileName) + 1];
strcpy(_fileName, fileName);
// Start scene with music not dimmed
_dimmed = false;
_dimmedTinsel = false;
_dimIteration = 0;
setVol(255);
}
void PCMMusicPlayer::setVolume(int volume) {
assert((volume >= 0) && (volume <= 100));
_dimmed = false;
setVol((volume * 255) / 100);
}
void PCMMusicPlayer::setVol(uint8 volume) {
_volume = volume;
_vm->_mixer->setChannelVolume(_handle, _volume);
}
bool PCMMusicPlayer::getMusicTinselDimmed() const {
return _dimmedTinsel;
}
void PCMMusicPlayer::dim(bool bTinselDim) {
if (_dimmed || (_volume == 0) ||
(_state == S_IDLE) || !_curChunk || (SysVar(SV_MUSICDIMFACTOR) == 0))
return;
_dimmed = true;
if (bTinselDim)
_dimmedTinsel = true;
_dimmedVolume = _volume - (_volume / SysVar(SV_MUSICDIMFACTOR));
// Iterate down, negative iteration
if (!_dimIteration)
_dimPosition = _volume;
_dimIteration = (_dimmedVolume - _volume)/DIM_SPEED;
debugC(DEBUG_DETAILED, kTinselDebugMusic, "Dimming music from %d to %d, steps %d", _dimPosition, _dimmedVolume, _dimIteration);
// And SFX
if (SysVar(SYS_SceneFxDimFactor))
_vm->_sound->setSFXVolumes(255 - 255/SysVar(SYS_SceneFxDimFactor));
}
void PCMMusicPlayer::unDim(bool bTinselUnDim) {
if (!_dimmed || (_dimmedTinsel && !bTinselUnDim))
return; // not dimmed
_dimmed = _dimmedTinsel = false;
if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
return;
// Iterate up, positive iteration
if (!_dimIteration)
_dimPosition = _dimmedVolume;
_dimIteration = (_volume - _dimmedVolume)/DIM_SPEED;
debugC(DEBUG_DETAILED, kTinselDebugMusic, "UnDimming music from %d to %d, steps %d", _dimPosition, _volume, _dimIteration);
// And SFX
_vm->_sound->setSFXVolumes(255);
}
void PCMMusicPlayer::dimIteration() {
if (_dimIteration != 0)
{
_dimPosition += _dimIteration;
if (_dimPosition >= _volume)
{
_dimPosition = _volume;
_dimIteration = 0;
}
else if (_dimPosition <= _dimmedVolume)
{
_dimPosition = _dimmedVolume;
_dimIteration = 0;
}
_vm->_mixer->setChannelVolume(_handle, _dimPosition);
}
}
void PCMMusicPlayer::startFadeOut(int ticks) {
if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
return;
debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Fading out music...");
if (_dimmed) {
// Start from dimmed volume and go from there
_dimmed = false;
_fadeOutVolume = _volume - _volume/SysVar(SV_MUSICDIMFACTOR);
} else
_fadeOutVolume = _volume;
assert(ticks != 0);
_fadeOutIteration = _fadeOutVolume / ticks;
fadeOutIteration();
}
void PCMMusicPlayer::fadeOutIteration() {
if ((_volume == 0) || (_state == S_IDLE) || !_curChunk)
return;
_fadeOutVolume = CLIP<int>(_fadeOutVolume -= _fadeOutIteration, 0, 255);
_vm->_mixer->setChannelVolume(_handle, _fadeOutVolume);
}
bool PCMMusicPlayer::getNextChunk() {
MusicSegment *musicSegments;
int32 *script, *scriptBuffer;
int id;
int snum;
uint32 sampleOffset, sampleLength, sampleCLength;
Common::File f;
byte *buffer;
Common::MemoryReadStream *sampleStream;
switch (_state) {
case S_NEW:
case S_NEXT:
_forcePlay = false;
script = scriptBuffer = (int32 *) LockMem(_hScript);
// Set parameters for this chunk of music
id = _scriptNum;
while(id--)
script = scriptBuffer + FROM_LE_32(*script);
snum = FROM_LE_32(script[_scriptIndex++]);
if (snum == MUSIC_JUMP || snum == MUSIC_END)
{
// Let usual code sort it out!
_scriptIndex--; // Undo increment
_forcePlay = true; // Force a Play
_state = S_END1; // 'Goto' S_END1
break;
}
musicSegments = (MusicSegment *) LockMem(_hSegment);
assert(FROM_LE_32(musicSegments[snum].numChannels) == 1);
assert(FROM_LE_32(musicSegments[snum].bitsPerSample) == 16);
sampleOffset = FROM_LE_32(musicSegments[snum].sampleOffset);
sampleLength = FROM_LE_32(musicSegments[snum].sampleLength);
sampleCLength = (((sampleLength + 63) & ~63)*33)/64;
if (!f.open(_fileName))
error(CANNOT_FIND_FILE, _fileName);
f.seek(sampleOffset);
if (f.ioFailed() || (uint32)f.pos() != sampleOffset)
error(FILE_IS_CORRUPT, _fileName);
buffer = (byte *) malloc(sampleCLength);
assert(buffer);
// read all of the sample
if (f.read(buffer, sampleCLength) != sampleCLength)
error(FILE_IS_CORRUPT, _fileName);
sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, true);
delete _curChunk;
_curChunk = makeADPCMStream(sampleStream, true, sampleCLength,
Audio::kADPCMTinsel8, 22050, 1, 32);
_state = S_MID;
return true;
case S_END1:
script = scriptBuffer = (int32 *) LockMem(_hScript);
id = _scriptNum;
while(id--)
script = scriptBuffer + FROM_LE_32(*script);
switch (script[_scriptIndex]) {
case MUSIC_END:
_state = S_END2;
break;
case MUSIC_JUMP:
_scriptIndex = script[++_scriptIndex];
// Fall through
default:
if (_forcePlay)
_state = S_NEW;
else
_state = S_NEXT;
_forcePlay = false;
break;
}
return true;
case S_END2:
_silenceSamples = 11025; // Half a second of silence
return true;
case S_END3:
stop();
_state = S_IDLE;
return false;
case S_IDLE:
return false;
default:
break;
}
return true;
}
void PCMMusicPlayer::play() {
if (_curChunk)
return;
if (_scriptNum == -1)
return;
_end = false;
getNextChunk();
}
void PCMMusicPlayer::stop() {
delete _curChunk;
_curChunk = 0;
_scriptNum = -1;
_state = S_IDLE;
_mState = S_IDLE;
_end = true;
}
void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop) { void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop) {
*pMidi = currentMidi; *pMidi = currentMidi;
*pLoop = currentLoop; *pLoop = currentLoop;
@ -495,9 +902,9 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
currentMidi = Midi; currentMidi = Midi;
currentLoop = Loop; currentLoop = Loop;
if (volMidi != 0 && Loop) { if (volMusic != 0 && Loop) {
PlayMidiSequence(currentMidi, true); PlayMidiSequence(currentMidi, true);
SetMidiVolume(volMidi); SetMidiVolume(volMusic);
} }
} }
@ -546,4 +953,4 @@ void dumpMusic() {
} }
#endif #endif
} // End of namespace Made } // End of namespace Tinsel

View file

@ -30,6 +30,8 @@
#include "sound/mididrv.h" #include "sound/mididrv.h"
#include "sound/midiparser.h" #include "sound/midiparser.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "common/mutex.h" #include "common/mutex.h"
namespace Tinsel { namespace Tinsel {
@ -58,11 +60,10 @@ SCNHANDLE GetTrackOffset(int trackNumber);
void dumpMusic(); void dumpMusic();
class MidiMusicPlayer : public MidiDriver {
class MusicPlayer : public MidiDriver {
public: public:
MusicPlayer(MidiDriver *driver); MidiMusicPlayer(MidiDriver *driver);
~MusicPlayer(); ~MidiMusicPlayer();
bool isPlaying() { return _isPlaying; } bool isPlaying() { return _isPlaying; }
void setPlaying(bool playing) { _isPlaying = playing; } void setPlaying(bool playing) { _isPlaying = playing; }
@ -71,6 +72,7 @@ public:
int getVolume() { return _masterVolume; } int getVolume() { return _masterVolume; }
void playXMIDI(byte *midiData, uint32 size, bool loop); void playXMIDI(byte *midiData, uint32 size, bool loop);
void stop(); void stop();
void pause(); void pause();
void resume(); void resume();
@ -111,6 +113,93 @@ protected:
byte _masterVolume; byte _masterVolume;
}; };
} // End of namespace Made class PCMMusicPlayer : public Audio::AudioStream {
public:
PCMMusicPlayer();
~PCMMusicPlayer();
bool isPlaying() const;
bool isDimmed() const;
void getTunePlaying(void *voidPtr, int length);
void restoreThatTune(void *voidPtr);
void setMusicSceneDetails(SCNHANDLE hScript, SCNHANDLE hSegment, const char *fileName);
void setVolume(int volume);
void startPlay(int id);
void stopPlay();
bool getMusicTinselDimmed() const;
void dim(bool bTinselDim);
void unDim(bool bTinselUnDim);
void dimIteration();
void startFadeOut(int ticks);
void fadeOutIteration();
int readBuffer(int16 *buffer, const int numSamples);
bool isStereo() const { return false; }
bool endOfData() const { return _end; }
bool endOfStream() const { return false; }
int getRate() const { return 22050; }
protected:
enum State {
S_IDLE,
S_NEW,
S_MID,
S_END1,
S_END2,
S_END3,
S_NEXT,
S_STOP
};
struct MusicSegment {
uint32 numChannels;
uint32 bitsPerSec;
uint32 bitsPerSample;
uint32 sampleLength;
uint32 sampleOffset;
};
Audio::SoundHandle _handle;
Audio::AudioStream *_curChunk;
Common::Mutex _mutex;
bool _end;
int _silenceSamples;
State _state, _mState;
bool _forcePlay;
int32 _scriptNum;
int32 _scriptIndex;
SCNHANDLE _hScript;
SCNHANDLE _hSegment;
char *_fileName;
uint8 _volume;
bool _dimmed;
bool _dimmedTinsel;
uint8 _dimmedVolume;
int _dimIteration;
int _dimPosition;
uint8 _fadeOutVolume;
int _fadeOutIteration;
void play();
void stop();
void setVol(uint8 volume);
bool getNextChunk();
};
} // End of namespace Tinsel
#endif #endif

View file

@ -29,14 +29,13 @@
#include "tinsel/cliprect.h" // object clip rect defs #include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/graphics.h" // low level interface #include "tinsel/graphics.h" // low level interface
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
#define OID_EFFECTS 0x2000 // generic special effects object id #define OID_EFFECTS 0x2000 // generic special effects object id
namespace Tinsel { namespace Tinsel {
/** screen clipping rectangle */
static const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// list of all objects // list of all objects
OBJECT *objectList = 0; OBJECT *objectList = 0;
@ -194,6 +193,7 @@ void InsertObject(OBJECT *pObjList, OBJECT *pInsObj) {
*/ */
void DelObject(OBJECT *pObjList, OBJECT *pDelObj) { void DelObject(OBJECT *pObjList, OBJECT *pDelObj) {
OBJECT *pPrev, *pObj; // object list traversal pointers OBJECT *pPrev, *pObj; // object list traversal pointers
const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// validate object pointer // validate object pointer
assert(pDelObj >= objectList && pDelObj <= objectList + NUM_OBJECTS - 1); assert(pDelObj >= objectList && pDelObj <= objectList + NUM_OBJECTS - 1);
@ -232,7 +232,9 @@ void DelObject(OBJECT *pObjList, OBJECT *pDelObj) {
} }
// if we get to here - object has not been found on the list // if we get to here - object has not been found on the list
error("DelObject(): formally 'assert(0)!'"); // This can be triggered in Act 3 in DW1 while talking to the guard,
// so this has been turned to a warning instead of an error
warning("DelObject(): formally 'assert(0)!'");
} }
@ -313,7 +315,7 @@ void GetAniOffset(SCNHANDLE hImg, int flags, int *pAniX, int *pAniY) {
// we are flipped vertically // we are flipped vertically
// set ani Y = -ani Y + height - 1 // set ani Y = -ani Y + height - 1
*pAniY = -*pAniY + FROM_LE_16(pImg->imgHeight) - 1; *pAniY = -*pAniY + (FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK) - 1;
} }
} else } else
// null image // null image
@ -365,21 +367,25 @@ OBJECT *InitObject(const OBJ_INIT *pInitTbl) {
// get pointer to image // get pointer to image
if (pInitTbl->hObjImg) { if (pInitTbl->hObjImg) {
int aniX, aniY; // objects animation offsets int aniX, aniY; // objects animation offsets
PALQ *pPalQ; // palette queue pointer PALQ *pPalQ = NULL; // palette queue pointer
const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image const IMAGE *pImg = (const IMAGE *)LockMem(pInitTbl->hObjImg); // handle to image
if (pImg->hImgPal) {
// allocate a palette for this object // allocate a palette for this object
pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal)); pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal));
// make sure palette allocated // make sure palette allocated
assert(pPalQ != NULL); assert(pPalQ != NULL);
}
// assign palette to object // assign palette to object
pObj->pPal = pPalQ; pObj->pPal = pPalQ;
// set objects size // set objects size
pObj->width = FROM_LE_16(pImg->imgWidth); pObj->width = FROM_LE_16(pImg->imgWidth);
pObj->height = FROM_LE_16(pImg->imgHeight); pObj->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK;
pObj->flags &= ~C16_FLAG_MASK;
pObj->flags |= FROM_LE_16(pImg->imgHeight) & C16_FLAG_MASK;
// set objects bitmap definition // set objects bitmap definition
pObj->hBits = FROM_LE_32(pImg->hImgBits); pObj->hBits = FROM_LE_32(pImg->hImgBits);
@ -434,7 +440,9 @@ void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) {
// setup new shape // setup new shape
pAniObj->width = FROM_LE_16(pNewImg->imgWidth); pAniObj->width = FROM_LE_16(pNewImg->imgWidth);
pAniObj->height = FROM_LE_16(pNewImg->imgHeight); pAniObj->height = FROM_LE_16(pNewImg->imgHeight) & ~C16_FLAG_MASK;
newflags &= ~C16_FLAG_MASK;
newflags |= pNewImg->imgHeight & C16_FLAG_MASK;
// set objects bitmap definition // set objects bitmap definition
pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits); pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits);

View file

@ -51,21 +51,24 @@ enum {
DMA_ABS = 0x0100, //!< position of object is absolute DMA_ABS = 0x0100, //!< position of object is absolute
DMA_CHANGED = 0x0200, //!< object has changed in some way since the last frame DMA_CHANGED = 0x0200, //!< object has changed in some way since the last frame
DMA_USERDEF = 0x0400, //!< user defined flags start here DMA_USERDEF = 0x0400, //!< user defined flags start here
DMA_GHOST = 0x0080,
/** flags that effect an objects appearance */ /** flags that effect an objects appearance */
DMA_HARDFLAGS = (DMA_WNZ | DMA_CNZ | DMA_CONST | DMA_WA | DMA_FLIPH | DMA_FLIPV | DMA_TRANS) DMA_HARDFLAGS = (DMA_WNZ | DMA_CNZ | DMA_CONST | DMA_WA | DMA_FLIPH | DMA_FLIPV | DMA_TRANS)
}; };
/** structure for image */ /** structure for image */
#include "common/pack-start.h" // START STRUCT PACKING
struct IMAGE { struct IMAGE {
short imgWidth; //!< image width short imgWidth; //!< image width
short imgHeight; //!< image height unsigned short imgHeight; //!< image height
short anioffX; //!< image x animation offset short anioffX; //!< image x animation offset
short anioffY; //!< image y animation offset short anioffY; //!< image y animation offset
SCNHANDLE hImgBits; //!< image bitmap handle SCNHANDLE hImgBits; //!< image bitmap handle
SCNHANDLE hImgPal; //!< image palette handle SCNHANDLE hImgPal; //!< image palette handle
}; } PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
/** a multi-object animation frame is a list of multi-image handles */ /** a multi-object animation frame is a list of multi-image handles */
typedef uint32 FRAME; typedef uint32 FRAME;
@ -93,6 +96,7 @@ struct OBJECT {
SCNHANDLE hMirror; //!< objects previous animation frame SCNHANDLE hMirror; //!< objects previous animation frame
int oid; //!< object identifier int oid; //!< object identifier
}; };
typedef OBJECT *POBJECT;
#include "common/pack-start.h" // START STRUCT PACKING #include "common/pack-start.h" // START STRUCT PACKING

View file

@ -28,6 +28,7 @@
#include "tinsel/graphics.h" #include "tinsel/graphics.h"
#include "tinsel/handle.h" // LockMem definition #include "tinsel/handle.h" // LockMem definition
#include "tinsel/palette.h" // palette allocator structures etc. #include "tinsel/palette.h" // palette allocator structures etc.
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
#include "common/system.h" #include "common/system.h"
@ -72,6 +73,17 @@ static VIDEO_DAC_Q *pDAChead;
/** the translucent palette lookup table */ /** the translucent palette lookup table */
uint8 transPalette[MAX_COLOURS]; // used in graphics.cpp uint8 transPalette[MAX_COLOURS]; // used in graphics.cpp
uint8 ghostPalette[MAX_COLOURS];
static int translucentIndex = 228;
static int talkIndex = 233;
static COLORREF talkColRef;
static COLORREF tagColRef;
#ifdef DEBUG #ifdef DEBUG
// diagnostic palette counters // diagnostic palette counters
static int numPals = 0; static int numPals = 0;
@ -243,6 +255,10 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
p->hPal = hNewPal; // set hardware palette data p->hPal = hNewPal; // set hardware palette data
p->numColours = FROM_LE_32(pNewPal->numColours); // set number of colours in palette p->numColours = FROM_LE_32(pNewPal->numColours); // set number of colours in palette
if (TinselV2)
// Copy all the colours
memcpy(p->palRGB, pNewPal->palRGB, pNewPal->numColours * sizeof(COLORREF));
#ifdef DEBUG #ifdef DEBUG
// one more palette in use // one more palette in use
if (++numPals > maxPals) if (++numPals > maxPals)
@ -250,6 +266,9 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
#endif #endif
// Q the change to the video DAC // Q the change to the video DAC
if (TinselV2)
UpdateDACqueue(p->posInDAC, pNewPal->numColours, p->palRGB);
else
UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal); UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal);
// move all palettes after this one down (if necessary) // move all palettes after this one down (if necessary)
@ -265,9 +284,14 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
+ pPrev->numColours | PALETTE_MOVED; + pPrev->numColours | PALETTE_MOVED;
// Q the palette change in position to the video DAC // Q the palette change in position to the video DAC
if (!TinselV2)
UpdateDACqueueHandle(pNxtPal->posInDAC, UpdateDACqueueHandle(pNxtPal->posInDAC,
pNxtPal->numColours, pNxtPal->numColours,
pNxtPal->hPal); pNxtPal->hPal);
else if (!pNxtPal->bFading)
UpdateDACqueue(pNxtPal->posInDAC,
pNxtPal->numColours,
pNxtPal->palRGB);
// update previous palette to current palette // update previous palette to current palette
pPrev = pNxtPal; pPrev = pNxtPal;
@ -347,10 +371,22 @@ void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
// install new palette // install new palette
pPalQ->hPal = hNewPal; pPalQ->hPal = hNewPal;
if (TinselV2) {
pPalQ->numColours = pNewPal->numColours;
// Copy all the colours
memcpy(pPalQ->palRGB, pNewPal->palRGB, pNewPal->numColours * sizeof(COLORREF));
if (!pPalQ->bFading)
// Q the change to the video DAC
UpdateDACqueue(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), pPalQ->palRGB);
} else {
// Q the change to the video DAC // Q the change to the video DAC
UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal); UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal);
}
} else { } else {
// # colours are different - will have to update all following palette entries // # colours are different - will have to update all following palette entries
assert(!TinselV2); // Fatal error for Tinsel 2
PALQ *pNxtPalQ; // next palette queue position PALQ *pNxtPalQ; // next palette queue position
@ -410,6 +446,33 @@ void SetBgndColour(COLORREF colour) {
UpdateDACqueue(BGND_DAC_INDEX, 1, &bgndColour); UpdateDACqueue(BGND_DAC_INDEX, 1, &bgndColour);
} }
/**
* Note whether a palette is being faded.
* @param pPalQ Palette queue position
* @param bFading Whether it is fading
*/
void FadingPalette(PPALQ pPalQ, bool bFading) {
// validate palette Q pointer
assert(pPalQ >= palAllocData && pPalQ <= palAllocData + NUM_PALETTES - 1);
// validate that this is a change
assert(pPalQ->bFading != bFading);
pPalQ->bFading = bFading;
}
/**
* All fading processes have just been killed, so none of the
* palettes are fading.
*/
void NoFadingPalettes(void) {
PPALQ pPalQ;
for (pPalQ = palAllocData; pPalQ <= palAllocData + NUM_PALETTES - 1; pPalQ++) {
pPalQ->bFading = false;
}
}
/** /**
* Builds the translucent palette from the current backgrounds palette. * Builds the translucent palette from the current backgrounds palette.
* @param hPalette Handle to current background palette * @param hPalette Handle to current background palette
@ -433,8 +496,134 @@ void CreateTranslucentPalette(SCNHANDLE hPalette) {
// map the Value field to one of the 4 colours reserved for the translucent palette // map the Value field to one of the 4 colours reserved for the translucent palette
val /= 63; val /= 63;
transPalette[i + 1] = (uint8)((val == 0) ? 0 : val + COL_HILIGHT - 1); transPalette[i + 1] = (uint8)((val == 0) ? 0 : val +
(TinselV2 ? TranslucentColour() : COL_HILIGHT) - 1);
} }
} }
/**
* Creates the ghost palette
*/
void CreateGhostPalette(SCNHANDLE hPalette) {
// get a pointer to the palette
PALETTE *pPal = (PALETTE *)LockMem(hPalette);
int i;
// leave background colour alone
ghostPalette[0] = 0;
for (i = 0; i < pPal->numColours; i++) {
// get the RGB colour model values
uint8 red = GetRValue(pPal->palRGB[i]);
uint8 green = GetGValue(pPal->palRGB[i]);
uint8 blue = GetBValue(pPal->palRGB[i]);
// calculate the Value field of the HSV colour model
unsigned val = (red > green) ? red : green;
val = (val > blue) ? val : blue;
// map the Value field to one of the 4 colours reserved for the translucent palette
val /= 64;
assert(/*val >= 0 &&*/ val <= 3);
ghostPalette[i + 1] = (uint8)(val + SysVar(ISV_GHOST_BASE));
}
}
/**
* Returns an adjusted colour RGB
* @param colour Colour to scale
*/
static COLORREF DimColour(COLORREF colour, int factor) {
uint32 red, green, blue;
if (factor == 10) {
// No change
return colour;
} else if (factor == 0) {
// No brightness
return 0;
} else {
// apply multiplier to RGB components
red = GetRValue(colour) * factor / 10;
green = GetGValue(colour) * factor / 10;
blue = GetBValue(colour) * factor / 10;
// return new colour
return RGB(red, green, blue);
}
}
/**
* DimPartPalette
*/
void DimPartPalette(SCNHANDLE hDimPal, int startColour, int length, int brightness) {
PALQ *pPalQ;
PALETTE *pDimPal;
int iColour;
pPalQ = FindPalette(hDimPal);
assert(pPalQ);
// get pointer to dim palette
pDimPal = (PALETTE *)LockMem(hDimPal);
// Adjust for the fact that palettes don't contain colour 0
startColour -= 1;
// Check some other things
if (startColour + length > pPalQ->numColours)
error("DimPartPalette(): colour overrun");
for (iColour = startColour ; iColour < startColour + length; iColour++) {
pPalQ->palRGB[iColour] = DimColour(pDimPal->palRGB[iColour], brightness);
}
if (!pPalQ->bFading) {
// Q the change to the video DAC
UpdateDACqueue(pPalQ->posInDAC + startColour, length, &pPalQ->palRGB[startColour]);
}
}
int TranslucentColour(void) {
return translucentIndex;
}
int HighlightColour(void) {
static COLORREF cRef;
cRef = (COLORREF)SysVar(SYS_HighlightRGB);
UpdateDACqueue(talkIndex, 1, &cRef);
return talkIndex;
}
int TalkColour(void) {
return TinselV2 ? talkIndex : TALKFONT_COL;
}
void SetTalkColourRef(COLORREF colRef) {
talkColRef = colRef;
}
COLORREF GetTalkColourRef(void) {
return talkColRef;
}
void SetTagColorRef(COLORREF colRef) {
tagColRef = colRef;
}
COLORREF GetTagColorRef(void) {
return tagColRef;
}
void SetTranslucencyOffset(int offset) {
translucentIndex = offset;
}
void SetTalkTextOffset(int offset) {
talkIndex = offset;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -43,7 +43,7 @@ enum {
MAX_COLOURS = 256, //!< maximum number of colours - for VGA 256 MAX_COLOURS = 256, //!< maximum number of colours - for VGA 256
BITS_PER_PIXEL = 8, //!< number of bits per pixel for VGA 256 BITS_PER_PIXEL = 8, //!< number of bits per pixel for VGA 256
MAX_INTENSITY = 255, //!< the biggest value R, G or B can have MAX_INTENSITY = 255, //!< the biggest value R, G or B can have
NUM_PALETTES = 3, //!< number of palettes NUM_PALETTES = 32, //!< number of palettes
// Discworld has some fixed apportioned bits in the palette. // Discworld has some fixed apportioned bits in the palette.
BGND_DAC_INDEX = 0, //!< index of background colour in Video DAC BGND_DAC_INDEX = 0, //!< index of background colour in Video DAC
@ -84,8 +84,11 @@ struct PALQ {
int objCount; //!< number of objects using this palette int objCount; //!< number of objects using this palette
int posInDAC; //!< palette position in the video DAC int posInDAC; //!< palette position in the video DAC
int numColours; //!< number of colours in the palette int numColours; //!< number of colours in the palette
// Discworld 2 fields
bool bFading; // Whether or not fading
COLORREF palRGB[MAX_COLOURS]; // actual palette colours
}; };
typedef PALQ *PPALQ;
#define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC" #define PALETTE_MOVED 0x8000 // when this bit is set in the "posInDAC"
// field - the palette entry has moved // field - the palette entry has moved
@ -137,8 +140,41 @@ COLORREF GetBgndColour(void); // returns current background colour
void SetBgndColour( // sets current background colour void SetBgndColour( // sets current background colour
COLORREF colour); // colour to set the background to COLORREF colour); // colour to set the background to
void FadingPalette(PPALQ pPalQ, bool bFading);
void CreateTranslucentPalette(SCNHANDLE BackPal); void CreateTranslucentPalette(SCNHANDLE BackPal);
void CreateGhostPalette(SCNHANDLE hPalette);
void NoFadingPalettes(void); // All fading processes have just been killed
void DimPartPalette(
SCNHANDLE hPal,
int startColour,
int length,
int brightness); // 0 = black, 10 == 100%
int TranslucentColour(void);
#define BoxColour TranslucentColour
int HighlightColour(void);
int TalkColour(void);
void SetTalkColourRef(COLORREF colRef);
COLORREF GetTalkColourRef(void);
void SetTagColorRef(COLORREF colRef);
COLORREF GetTagColorRef(void);
void SetTalkTextOffset(int offset);
void SetTranslucencyOffset(int offset);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_PALETTE_H #endif // TINSEL_PALETTE_H

View file

@ -25,13 +25,16 @@
*/ */
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/drives.h"
#include "tinsel/events.h" // 'POINTED' etc. #include "tinsel/events.h" // 'POINTED' etc.
#include "tinsel/handle.h" // LockMem() #include "tinsel/handle.h" // LockMem()
#include "tinsel/inventory.h" // for inventory id's #include "tinsel/dialogs.h" // for inventory id's
#include "tinsel/pcode.h" // opcodes etc. #include "tinsel/pcode.h" // opcodes etc.
#include "tinsel/scn.h" // FindChunk() #include "tinsel/scn.h" // FindChunk()
#include "tinsel/serializer.h" #include "tinsel/serializer.h"
#include "tinsel/timers.h"
#include "tinsel/tinlib.h" // Library routines #include "tinsel/tinlib.h" // Library routines
#include "tinsel/tinsel.h"
#include "common/util.h" #include "common/util.h"
@ -97,7 +100,7 @@ enum OPCODE {
#define OPMASK 0x3F //!< mask to isolate the opcode #define OPMASK 0x3F //!< mask to isolate the opcode
bool bNoPause = false;
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
@ -107,13 +110,19 @@ static int numGlobals = 0; // How many global variables to save/restore
static INT_CONTEXT *icList = 0; static INT_CONTEXT *icList = 0;
static uint32 hMasterScript;
/** /**
* Keeps the code array pointer up to date. * Keeps the code array pointer up to date.
*/ */
void LockCode(INT_CONTEXT *ic) { void LockCode(INT_CONTEXT *ic) {
if (ic->GSort == GS_MASTER) if (ic->GSort == GS_MASTER) {
ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE); if (TinselV2)
// Get the srcipt handle from a specific global chunk
ic->code = (byte *)LockMem(hMasterScript);
else else
ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
} else
ic->code = (byte *)LockMem(ic->hCode); ic->code = (byte *)LockMem(ic->hCode);
} }
@ -124,7 +133,7 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
INT_CONTEXT *pic; INT_CONTEXT *pic;
int i; int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) { for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort == GS_NONE) { if (pic->GSort == GS_NONE) {
pic->pProc = g_scheduler->getCurrentProcess(); pic->pProc = g_scheduler->getCurrentProcess();
pic->GSort = gsort; pic->GSort = gsort;
@ -141,11 +150,41 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
error("Out of interpret contexts"); error("Out of interpret contexts");
} }
static void FreeWaitCheck(PINT_CONTEXT pic, bool bVoluntary) {
int i;
// Is this waiting for something?
if (pic->waitNumber1) {
for (i = 0; i < NUM_INTERPRET; i++) {
if ((icList + i)->waitNumber2 == pic->waitNumber1) {
(icList + i)->waitNumber2 = 0;
break;
}
}
}
// Is someone waiting for this?
if (pic->waitNumber2) {
for (i = 0; i < NUM_INTERPRET; i++) {
if ((icList + i)->waitNumber1 == pic->waitNumber2) {
(icList + i)->waitNumber1 = 0;
(icList + i)->resumeCode = bVoluntary ? RES_FINISHED : RES_CUTSHORT;
g_scheduler->reschedule((icList + i)->pProc);
break;
}
}
assert(i < NUM_INTERPRET);
}
}
/** /**
* Normal release of an interpret context. * Normal release of an interpret context.
* Called from the end of Interpret(). * Called from the end of Interpret().
*/ */
static void FreeInterpretContextPi(INT_CONTEXT *pic) { static void FreeInterpretContextPi(INT_CONTEXT *pic) {
FreeWaitCheck(pic, true);
if (TinselV2)
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE; pic->GSort = GS_NONE;
} }
@ -158,8 +197,11 @@ void FreeInterpretContextPr(PROCESS *pProc) {
INT_CONTEXT *pic; INT_CONTEXT *pic;
int i; int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) { for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort != GS_NONE && pic->pProc == pProc) { if (pic->GSort != GS_NONE && pic->pProc == pProc) {
FreeWaitCheck(pic, false);
if (TinselV2)
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE; pic->GSort = GS_NONE;
break; break;
} }
@ -173,8 +215,9 @@ void FreeMostInterpretContexts(void) {
INT_CONTEXT *pic; INT_CONTEXT *pic;
int i; int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) { for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort != GS_MASTER) { if ((pic->GSort != GS_MASTER) && (pic->GSort != GS_GPROCESS)) {
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE; pic->GSort = GS_NONE;
} }
} }
@ -187,8 +230,9 @@ void FreeMasterInterpretContext(void) {
INT_CONTEXT *pic; INT_CONTEXT *pic;
int i; int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) { for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if (pic->GSort == GS_MASTER) { if ((pic->GSort == GS_MASTER) || (pic->GSort == GS_GPROCESS)) {
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE; pic->GSort = GS_NONE;
return; return;
} }
@ -205,8 +249,8 @@ void FreeMasterInterpretContext(void) {
* @param actorId Associated actor (if any) * @param actorId Associated actor (if any)
* @param pinvo Associated inventory object * @param pinvo Associated inventory object
*/ */
INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event, INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, TINSEL_EVENT event,
HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo) { HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
INT_CONTEXT *ic; INT_CONTEXT *ic;
ic = AllocateInterpretContext(gsort); ic = AllocateInterpretContext(gsort);
@ -215,14 +259,14 @@ INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event
ic->hCode = hCode; ic->hCode = hCode;
LockCode(ic); LockCode(ic);
ic->event = event; ic->event = event;
ic->hpoly = hpoly; ic->hPoly = hpoly;
ic->actorid = actorid; ic->idActor = actorid;
ic->pinvo = pinvo; ic->pinvo = pinvo;
// Previously local variables in Interpret() // Previously local variables in Interpret()
ic->bHalt = false; // set to exit interpeter ic->bHalt = false; // set to exit interpeter
ic->escOn = false; ic->escOn = myEscape > 0;
ic->myescEvent = 0; // only initialised to prevent compiler warning! ic->myEscape = myEscape;
ic->sp = 0; ic->sp = 0;
ic->bp = ic->sp + 1; ic->bp = ic->sp + 1;
ic->ip = 0; // start of code ic->ip = 0; // start of code
@ -256,6 +300,9 @@ void RegisterGlobals(int num) {
if (pGlobals == NULL) { if (pGlobals == NULL) {
numGlobals = num; numGlobals = num;
hMasterScript = !TinselV2 ? 0 :
READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));
// Allocate RAM for pGlobals and make sure it's allocated // Allocate RAM for pGlobals and make sure it's allocated
pGlobals = (int32 *)calloc(numGlobals, sizeof(int32)); pGlobals = (int32 *)calloc(numGlobals, sizeof(int32));
if (pGlobals == NULL) { if (pGlobals == NULL) {
@ -263,18 +310,38 @@ void RegisterGlobals(int num) {
} }
// Allocate RAM for interpret contexts and make sure it's allocated // Allocate RAM for interpret contexts and make sure it's allocated
icList = (INT_CONTEXT *)calloc(MAX_INTERPRET, sizeof(INT_CONTEXT)); icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT));
if (icList == NULL) { if (icList == NULL) {
error("Cannot allocate memory for interpret contexts"); error("Cannot allocate memory for interpret contexts");
} }
g_scheduler->setResourceCallback(FreeInterpretContextPr); g_scheduler->setResourceCallback(FreeInterpretContextPr);
} else { } else {
// Check size is still the same // Check size is still the same
assert(numGlobals == num); assert(numGlobals == num);
memset(pGlobals, 0, numGlobals * sizeof(int32)); memset(pGlobals, 0, numGlobals * sizeof(int32));
memset(icList, 0, MAX_INTERPRET * sizeof(INT_CONTEXT)); memset(icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT));
}
if (TinselV2) {
// read initial values
CdCD(nullContext);
Common::File f;
if (!f.open(GLOBALS_FILENAME))
error(CANNOT_FIND_FILE, GLOBALS_FILENAME);
int32 length = f.readSint32LE();
if (length != num)
error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
for (int i = 0; i < length; ++i)
pGlobals[i] = f.readSint32LE();
if (f.ioFailed())
error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
f.close();
} }
} }
@ -309,8 +376,8 @@ void INT_CONTEXT::syncWithSerializer(Serializer &s) {
s.syncAsUint32LE(GSort); s.syncAsUint32LE(GSort);
s.syncAsUint32LE(hCode); s.syncAsUint32LE(hCode);
s.syncAsUint32LE(event); s.syncAsUint32LE(event);
s.syncAsSint32LE(hpoly); s.syncAsSint32LE(hPoly);
s.syncAsSint32LE(actorid); s.syncAsSint32LE(idActor);
for (int i = 0; i < PCODE_STACK_SIZE; ++i) for (int i = 0; i < PCODE_STACK_SIZE; ++i)
s.syncAsSint32LE(stack[i]); s.syncAsSint32LE(stack[i]);
@ -320,14 +387,14 @@ void INT_CONTEXT::syncWithSerializer(Serializer &s) {
s.syncAsSint32LE(ip); s.syncAsSint32LE(ip);
s.syncAsUint32LE(bHalt); s.syncAsUint32LE(bHalt);
s.syncAsUint32LE(escOn); s.syncAsUint32LE(escOn);
s.syncAsSint32LE(myescEvent); s.syncAsSint32LE(myEscape);
} }
/** /**
* Return pointer to and size of global data for save/restore game. * Return pointer to and size of global data for save/restore game.
*/ */
void SaveInterpretContexts(INT_CONTEXT *sICInfo) { void SaveInterpretContexts(INT_CONTEXT *sICInfo) {
memcpy(sICInfo, icList, MAX_INTERPRET * sizeof(INT_CONTEXT)); memcpy(sICInfo, icList, NUM_INTERPRET * sizeof(INT_CONTEXT));
} }
/** /**
@ -451,6 +518,8 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
return; return;
ic->sp += tmp2; ic->sp += tmp2;
LockCode(ic); LockCode(ic);
if (TinselV2 && (ic->resumeState == RES_1))
ic->resumeState = RES_NOT;
break; break;
case OP_RET: // procedure return case OP_RET: // procedure return
@ -567,12 +636,14 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
break; break;
case OP_ESCON: case OP_ESCON:
bNoPause = true;
ic->escOn = true; ic->escOn = true;
ic->myescEvent = GetEscEvents(); ic->myEscape = GetEscEvents();
break; break;
case OP_ESCOFF: case OP_ESCOFF:
ic->escOn = false; ic->escOn = false;
ic->myEscape = 0;
break; break;
default: default:
@ -590,4 +661,128 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
FreeInterpretContextPi(ic); FreeInterpretContextPi(ic);
} }
/**
* Associates an interpret context with the
* process that will run it.
*/
void AttachInterpret(INT_CONTEXT *pic, PROCESS *pProc) {
// Attach the process which is using this context
pic->pProc = pProc;
}
/**
* Generate a number that isn't being used.
*/
static uint32 UniqueWaitNumber(void) {
uint32 retval;
int i;
for (retval = DwGetCurrentTime(); 1; retval--) {
if (retval == 0)
retval = (uint32)-1;
for (i = 0; i < NUM_INTERPRET; i++) {
if ((icList+i)->waitNumber1 == retval
|| (icList+i)->waitNumber2 == retval)
break;
}
if (i == NUM_INTERPRET)
return retval;
}
}
/**
* WaitInterpret
*/
void WaitInterpret(CORO_PARAM, PPROCESS pWaitProc, bool *result) {
int i;
PPROCESS currentProcess = g_scheduler->getCurrentProcess();
assert(currentProcess);
assert(currentProcess != pWaitProc);
if (result) *result = false;
/*
* Calling process is the waiter, find its interpret context.
*/
CORO_BEGIN_CONTEXT;
PINT_CONTEXT picWaiter, picWaitee;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
for (i = 0, _ctx->picWaiter = icList; i < NUM_INTERPRET; i++, _ctx->picWaiter++) {
if (_ctx->picWaiter->GSort != GS_NONE && _ctx->picWaiter->pProc == currentProcess) {
break;
}
}
/*
* Find the interpret context of the process we're waiting for
*/
for (i = 0, _ctx->picWaitee = icList; i < NUM_INTERPRET; i++, _ctx->picWaitee++) {
if (_ctx->picWaitee->GSort != GS_NONE && _ctx->picWaitee->pProc == pWaitProc) {
break;
}
}
/*
* Set the first as waiting for the second
*/
assert(_ctx->picWaitee->waitNumber2 == 0);
_ctx->picWaiter->waitNumber1 = _ctx->picWaitee->waitNumber2 = UniqueWaitNumber();
_ctx->picWaiter->resumeCode = RES_WAITING;
/*
* Wait for it
*/
CORO_GIVE_WAY;
while (_ctx->picWaiter->resumeCode == RES_WAITING) {
CORO_SLEEP(1);
}
if (result)
*result = (_ctx->picWaiter->resumeCode == RES_FINISHED);
CORO_END_CODE;
}
/**
* CheckOutWaiters
*/
void CheckOutWaiters(void) {
int i, j;
// Check all waited for have someone waiting
for (i = 0; i < NUM_INTERPRET; i++) {
// If someone is supposedly waiting for this one
if ((icList + i)->GSort != GS_NONE && (icList + i)->waitNumber2) {
// Someone really must be waiting for this one
for (j = 0; j < NUM_INTERPRET; j++) {
if ((icList + j)->GSort != GS_NONE
&& (icList + j)->waitNumber1 == (icList + i)->waitNumber2) {
break;
}
}
assert(j < NUM_INTERPRET);
}
}
// Check waiting for someone to wait for
for (i = 0; i < NUM_INTERPRET; i++) {
// If someone is supposedly waiting for this one
if ((icList + i)->GSort != GS_NONE && (icList + i)->waitNumber1) {
// Someone really must be waiting for this one
for (j = 0; j < NUM_INTERPRET; j++) {
if ((icList + j)->GSort != GS_NONE
&& (icList + j)->waitNumber2 == (icList + i)->waitNumber1) {
break;
}
}
assert(j < NUM_INTERPRET);
}
}
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -27,7 +27,7 @@
#ifndef TINSEL_PCODE_H // prevent multiple includes #ifndef TINSEL_PCODE_H // prevent multiple includes
#define TINSEL_PCODE_H #define TINSEL_PCODE_H
#include "tinsel/events.h" // for USER_EVENT #include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/sched.h" // for PROCESS #include "tinsel/sched.h" // for PROCESS
namespace Tinsel { namespace Tinsel {
@ -45,9 +45,12 @@ enum {
}; };
enum GSORT { enum GSORT {
GS_NONE, GS_ACTOR, GS_MASTER, GS_POLYGON, GS_INVENTORY, GS_SCENE GS_NONE, GS_ACTOR, GS_MASTER, GS_POLYGON, GS_INVENTORY, GS_SCENE,
GS_PROCESS, GS_GPROCESS
}; };
enum RESCODE {RES_WAITING, RES_FINISHED, RES_CUTSHORT};
struct INT_CONTEXT { struct INT_CONTEXT {
// Elements for interpret context management // Elements for interpret context management
@ -57,9 +60,9 @@ struct INT_CONTEXT {
// Previously parameters to Interpret() // Previously parameters to Interpret()
SCNHANDLE hCode; //!< scene handle of the code to execute SCNHANDLE hCode; //!< scene handle of the code to execute
byte *code; //!< pointer to the code to execute byte *code; //!< pointer to the code to execute
USER_EVENT event; //!< causal event TINSEL_EVENT event; //!< causal event
HPOLYGON hpoly; //!< associated polygon (if any) HPOLYGON hPoly; //!< associated polygon (if any)
int actorid; //!< associated actor (if any) int idActor; //!< associated actor (if any)
INV_OBJECT *pinvo; //!< associated inventory object INV_OBJECT *pinvo; //!< associated inventory object
// Previously local variables in Interpret() // Previously local variables in Interpret()
@ -69,27 +72,32 @@ struct INT_CONTEXT {
int ip; //!< instruction pointer int ip; //!< instruction pointer
bool bHalt; //!< set to exit interpeter bool bHalt; //!< set to exit interpeter
bool escOn; bool escOn;
int myescEvent; //!< only initialised to prevent compiler warning! int myEscape; //!< only initialised to prevent compiler warning!
uint32 waitNumber1; // The waiting numbert
uint32 waitNumber2; // The wait for number
RESCODE resumeCode;
RESUME_STATE resumeState; RESUME_STATE resumeState;
void syncWithSerializer(Serializer &s); void syncWithSerializer(Serializer &s);
}; };
typedef INT_CONTEXT *PINT_CONTEXT;
/*----------------------------------------------------------------------*\ /*----------------------------------------------------------------------*\
|* Interpreter Function Prototypes *| |* Interpreter Function Prototypes *|
\*----------------------------------------------------------------------*/ \*----------------------------------------------------------------------*/
void Interpret(CORO_PARAM, INT_CONTEXT *ic); // Interprets the PCODE instructions in the code array // Interprets the PCODE instructions in the code array
void Interpret(CORO_PARAM, INT_CONTEXT *ic);
INT_CONTEXT *InitInterpretContext( INT_CONTEXT *InitInterpretContext(
GSORT gsort, GSORT gsort,
SCNHANDLE hCode, // code to execute SCNHANDLE hCode, // code to execute
USER_EVENT event, // causal event TINSEL_EVENT event, // causal event
HPOLYGON hpoly, // associated polygon (if any) HPOLYGON hpoly, // associated polygon (if any)
int actorid, // associated actor (if any) int actorid, // associated actor (if any)
INV_OBJECT *pinvo); // associated inventory object INV_OBJECT *pinvo,
int myEscape = -1); // associated inventory object
INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric); INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric);
@ -101,8 +109,12 @@ void SaveInterpretContexts(INT_CONTEXT *sICInfo);
void RegisterGlobals(int num); void RegisterGlobals(int num);
void FreeGlobals(void); void FreeGlobals(void);
void AttachInterpret(INT_CONTEXT *pic, PROCESS *pProc);
#define MAX_INTERPRET (NUM_PROCESS - 20) void WaitInterpret(CORO_PARAM, PPROCESS pWaitProc, bool *result);
#define NUM_INTERPRET (NUM_PROCESS - 20)
#define MAX_INTERPRET (MAX_PROCESSES - 20)
/*----------------------------------------------------------------------*\ /*----------------------------------------------------------------------*\
|* Library Procedure and Function codes parameter enums *| |* Library Procedure and Function codes parameter enums *|
@ -137,10 +149,6 @@ void FreeGlobals(void);
#define MIDI_DEF 0 #define MIDI_DEF 0
#define MIDI_LOOP 1 #define MIDI_LOOP 1
#define TRANS_DEF 0
#define TRANS_CUT 1
#define TRANS_FADE 2
#define FM_IN 0 // #define FM_IN 0 //
#define FM_OUT 1 // fademidi() #define FM_OUT 1 // fademidi()

View file

@ -28,6 +28,7 @@
#include "tinsel/actors.h" #include "tinsel/actors.h"
#include "tinsel/background.h" #include "tinsel/background.h"
#include "tinsel/coroutine.h"
#include "tinsel/cursor.h" #include "tinsel/cursor.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/events.h" #include "tinsel/events.h"
@ -41,6 +42,7 @@
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/strres.h" #include "tinsel/strres.h"
#include "tinsel/text.h" #include "tinsel/text.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -56,8 +58,8 @@ extern int newestString; // The overrun counter, in STRRES.C
//----------------- EXTERNAL FUNCTIONS --------------------- //----------------- EXTERNAL FUNCTIONS ---------------------
// in BG.C // in BG.C
extern int BackgroundWidth(void); extern int BgWidth(void);
extern int BackgroundHeight(void); extern int BgHeight(void);
@ -84,8 +86,11 @@ static bool bShowString = false;
static int TaggedActor = 0; static int TaggedActor = 0;
static HPOLYGON hTaggedPolygon = NOPOLY; static HPOLYGON hTaggedPolygon = NOPOLY;
static enum { TAGS_OFF, TAGS_ON } TagsActive = TAGS_ON; static bool bTagsActive = true;
static bool bPointingActive = true;
static char tagBuffer[64];
#ifdef DEBUG #ifdef DEBUG
/** /**
@ -134,7 +139,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
char PositionString[64]; // sprintf() things into here char PositionString[64]; // sprintf() things into here
PMACTOR pActor; // Lead actor PMOVER pActor; // Lead actor
while (1) { while (1) {
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
@ -159,7 +164,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// New text objects // New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset); sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
_ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString, _ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, hTagFontHandle(), TXT_CENTRE); 0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
if (DispPath) { if (DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH); HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
if (hp == NOPOLY) if (hp == NOPOLY)
@ -171,7 +176,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
PolyCornerX(hp, 2), PolyCornerY(hp, 2), PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3)); PolyCornerX(hp, 3), PolyCornerY(hp, 3));
_ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString, _ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, hTagFontHandle(), 0); 0, 4, POSY+ 10, GetTagFontHandle(), 0);
} }
// update previous position // update previous position
@ -191,7 +196,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "%d", Overrun); sprintf(PositionString, "%d", Overrun);
_ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString, _ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, OPOSX, POSY, hTagFontHandle(), TXT_CENTRE); 0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous value // update previous value
_ctx->prevOver = Overrun; _ctx->prevOver = Overrun;
@ -202,7 +207,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
| Lead actor's position. | | Lead actor's position. |
\*----------------------*/ \*----------------------*/
pActor = GetMover(LEAD_ACTOR); pActor = GetMover(LEAD_ACTOR);
if (pActor && pActor->MActorState == NORM_MACTOR) { if (pActor && pActor->MActorState == NORM_MOVER) {
// get lead's animation position // get lead's animation position
GetActorPos(LEAD_ACTOR, &aniX, &aniY); GetActorPos(LEAD_ACTOR, &aniX, &aniY);
@ -217,7 +222,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// create new text object list // create new text object list
sprintf(PositionString, "%d %d", aniX, aniY); sprintf(PositionString, "%d %d", aniX, aniY);
_ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString, _ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, hTagFontHandle(), TXT_CENTRE); 0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous position // update previous position
_ctx->prevlX = aniX; _ctx->prevlX = aniX;
@ -236,7 +241,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "String: %d", newestString); sprintf(PositionString, "String: %d", newestString);
_ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString, _ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, hTalkFontHandle(), TXT_CENTRE); 0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
// update previous value // update previous value
_ctx->prevString = newestString; _ctx->prevString = newestString;
@ -252,9 +257,52 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
} }
#endif #endif
/**
* While inventory/menu is open.
*/
void DisablePointing(CORO_PARAM) {
CORO_BEGIN_CONTEXT;
int i;
HPOLYGON hPoly; // Polygon handle
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
bPointingActive = false;
for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
_ctx->hPoly = GetPolyHandle(_ctx->i);
if (_ctx->hPoly != NOPOLY && PolyType(_ctx->hPoly) == TAG && PolyIsPointedTo(_ctx->hPoly)) {
SetPolyPointedTo(_ctx->hPoly, false);
SetPolyTagWanted(_ctx->hPoly, false, false, 0);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
}
}
// For each tagged actor
for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
if (ActorIsPointedTo(_ctx->i)) {
SetActorPointedTo(_ctx->i, false);
SetActorTagWanted(_ctx->i, false, false, 0);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
}
}
CORO_END_CODE;
}
/**
* EnablePointing()
*/
void EnablePointing(void) {
bPointingActive = true;
}
/** /**
* Tag process keeps us updated as to which tagged actor is currently tagged * Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does User_Event(). * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/ */
static void SaveTaggedActor(int ano) { static void SaveTaggedActor(int ano) {
TaggedActor = ano; TaggedActor = ano;
@ -262,7 +310,7 @@ static void SaveTaggedActor(int ano) {
/** /**
* Tag process keeps us updated as to which tagged actor is currently tagged * Tag process keeps us updated as to which tagged actor is currently tagged
* (if one is). Tag process asks us for this information, as does User_Event(). * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/ */
int GetTaggedActor(void) { int GetTaggedActor(void) {
return TaggedActor; return TaggedActor;
@ -270,7 +318,7 @@ int GetTaggedActor(void) {
/** /**
* Tag process keeps us updated as to which polygon is currently tagged * Tag process keeps us updated as to which polygon is currently tagged
* (if one is). Tag process asks us for this information, as does User_Event(). * (if one is). Tag process asks us for this information, as does ProcessUserEvent().
*/ */
static void SaveTaggedPoly(HPOLYGON hp) { static void SaveTaggedPoly(HPOLYGON hp) {
hTaggedPolygon = hp; hTaggedPolygon = hp;
@ -341,12 +389,59 @@ static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
* the screen. * the screen.
*/ */
static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) { static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
static int Loffset = 0, Toffset = 0; // Values when tag was displayed static int tagX = 0, tagY = 0; // Values when tag was displayed
int nLoff, nToff; // new values, to keep tag in place int newX, newY; // new values, to keep tag in place
int ano; int ano;
int xtext, ytext; int xtext, ytext;
bool newActor; bool newActor;
if (TinselV2) {
// Tinsel 2 version
// Get the foremost pointed to actor
int actor = FrontTaggedActor();
if (actor == 0) {
SaveTaggedActor(0);
return false;
}
// If new actor
// or actor has suddenly decided it wants tagging...
if (actor != GetTaggedActor() || (ActorTagIsWanted(actor) && !*ppText)) {
// Put up actor tag
SaveTaggedActor(actor); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
if (ActorTagIsWanted(actor)) {
GetActorTagPos(actor, &tagX, &tagY, false);
LoadStringRes(GetActorTagHandle(actor), tagBuffer, sizeof(tagBuffer));
// May have buggered cursor
EndCursorFollowed();
*ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), tagBuffer,
0, tagX, tagY, GetTagFontHandle(), TXT_CENTRE, 0);
assert(*ppText);
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else
*ppText = NULL;
} else if (*ppText) {
// Same actor, maintain tag position
GetActorTagPos(actor, &newX, &newY, false);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, newX - tagX, newY - tagY);
tagX = newX;
tagY = newY;
}
}
return true;
}
// Tinsel 1 version
// For each actor with a tag.... // For each actor with a tag....
FirstTaggedActor(); FirstTaggedActor();
while ((ano = NextTaggedActor()) != 0) { while ((ano = NextTaggedActor()) != 0) {
@ -369,20 +464,20 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedActor(ano); // This actor tagged SaveTaggedActor(ano); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon SaveTaggedPoly(NOPOLY); // No tagged polygon
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(GetActorTag(ano), tBufferAddr(), TBUFSZ); LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), *ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, xtext - Loffset, ytext - Toffset, hTagFontHandle(), TXT_CENTRE); 0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Actor tag string produced NULL text assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT); MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else { } else {
// Maintain actor tag's position // Maintain actor tag's position
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff); PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
if (nLoff != Loffset || nToff != Toffset) { if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff); MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
Loffset = nLoff; tagX = newX;
Toffset = nToff; tagY = newY;
} }
} }
return true; return true;
@ -406,6 +501,7 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
*/ */
static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) { static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
static int Loffset = 0, Toffset = 0; // Values when tag was displayed static int Loffset = 0, Toffset = 0; // Values when tag was displayed
static int curX = 0, curY = 0;
int nLoff, nToff; // new values, to keep tag in place int nLoff, nToff; // new values, to keep tag in place
HPOLYGON hp; HPOLYGON hp;
bool newPoly; bool newPoly;
@ -418,8 +514,11 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
for (int i = 0; i < MAX_POLY; i++) { for (int i = 0; i < MAX_POLY; i++) {
hp = GetPolyHandle(i); hp = GetPolyHandle(i);
if (TinselV2 && (hp == NOPOLY))
continue;
// Added code for un-tagged tags // Added code for un-tagged tags
if (hp != NOPOLY && PolyPointState(hp) == POINTING && PolyTagState(hp) != TAG_ON) { if ((hp != NOPOLY) && (PolyPointState(hp) == PS_POINTING) && (PolyTagState(hp) != TAG_ON)) {
// This poly is entitled to be tagged // This poly is entitled to be tagged
if (hp != GetTaggedPoly()) { if (hp != GetTaggedPoly()) {
if (*ppText) { if (*ppText) {
@ -431,58 +530,99 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedPoly(hp); // This polygon tagged SaveTaggedPoly(hp); // This polygon tagged
} }
return true; return true;
} else if (hp != NOPOLY && PolyTagState(hp) == TAG_ON) { } else if ((TinselV2 && PolyTagIsWanted(hp)) ||
(!TinselV2 && hp != NOPOLY && PolyTagState(hp) == TAG_ON)) {
// Put up or maintain polygon tag // Put up or maintain polygon tag
newPoly = false;
if (TinselV2) {
if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
} else {
if (*pTag != POLY_HOTSPOT_TAG) if (*pTag != POLY_HOTSPOT_TAG)
newPoly = true; // A new polygon (no current) newPoly = true; // A new polygon (no current)
else if (hp != GetTaggedPoly()) else if (hp != GetTaggedPoly())
newPoly = true; // Different polygon newPoly = true; // Different polygon
else }
newPoly = false; // Same polygon
if (newPoly) { if (newPoly) {
if (*ppText) if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText); MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
if (!TinselV2)
*pTag = POLY_HOTSPOT_TAG; *pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged SaveTaggedPoly(hp); // This polygon tagged
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
getPolyTagInfo(hp, &hTagtext, &tagx, &tagy); GetTagTag(hp, &hTagtext, &tagx, &tagy);
int strLen; int strLen;
if (PolyTagHandle(hp) != 0) if (GetPolyTagHandle(hp) != 0)
strLen = LoadStringRes(PolyTagHandle(hp), tBufferAddr(), TBUFSZ); strLen = LoadStringRes(GetPolyTagHandle(hp), TextBufferAddr(), TBUFSZ);
else else
strLen = LoadStringRes(hTagtext, tBufferAddr(), TBUFSZ); strLen = LoadStringRes(hTagtext, TextBufferAddr(), TBUFSZ);
if (strLen == 0) if (strLen == 0)
// No valid string returned, so leave ppText as NULL // No valid string returned, so leave ppText as NULL
ppText = NULL; ppText = NULL;
else { else if (TinselV2 && !PolyTagFollowsCursor(hp)) {
// Handle displaying the tag text on-screen // May have buggered cursor
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(), EndCursorFollowed();
0, tagx - Loffset, tagy - Toffset,
hTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
*ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS),
TextBufferAddr(), 0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE, 0);
} else if (TinselV2) {
// Bugger cursor
const char *tagPtr = TextBufferAddr();
if (tagPtr[0] < ' ' && tagPtr[1] == EOS_CHAR)
StartCursorFollowed();
GetCursorXYNoWait(&curX, &curY, false);
*ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, curX, curY, GetTagFontHandle(), TXT_CENTRE, 0);
} else {
// Handle displaying the tag text on-screen
*ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
}
// DW1 has some tags without text, e.g. the "equals" button when talking to the guard in act 3
if (ppText) {
MultiSetZPosition(*ppText, Z_TAG_TEXT);
/* /*
* New feature: Don't go off the side of the background * New feature: Don't go off the side of the background
*/ */
shift = MultiRightmost(*ppText) + Loffset + 2; shift = MultiRightmost(*ppText) + Loffset + 2;
if (shift >= BackgroundWidth()) // Not off right if (shift >= BgWidth()) // Not off right
MultiMoveRelXY(*ppText, BackgroundWidth() - shift, 0); MultiMoveRelXY(*ppText, BgWidth() - shift, 0);
shift = MultiLeftmost(*ppText) + Loffset - 1; shift = MultiLeftmost(*ppText) + Loffset - 1;
if (shift <= 0) // Not off left if (shift <= 0) // Not off left
MultiMoveRelXY(*ppText, -shift, 0); MultiMoveRelXY(*ppText, -shift, 0);
shift = MultiLowest(*ppText) + Toffset; shift = MultiLowest(*ppText) + Toffset;
if (shift > BackgroundHeight()) // Not off bottom if (shift > BgHeight()) // Not off bottom
MultiMoveRelXY(*ppText, 0, BackgroundHeight() - shift); MultiMoveRelXY(*ppText, 0, BgHeight() - shift);
}
} else if (TinselV2 && (*ppText)) {
if (!PolyTagFollowsCursor(hp)) {
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
} }
} else { } else {
GetCursorXY(&tagx, &tagy, false);
if (tagx != curX || tagy != curY) {
MultiMoveRelXY(*ppText, tagx - curX, tagy - curY);
curX = tagx;
curY = tagy;
}
}
} else if (!TinselV2) {
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff); PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) { if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff); MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
@ -495,7 +635,9 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
} }
// No tagged polygon // No tagged polygon
if (*pTag == POLY_HOTSPOT_TAG) { if (TinselV2)
SaveTaggedPoly(NOPOLY);
else if (*pTag == POLY_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG; *pTag = NO_HOTSPOT_TAG;
SaveTaggedPoly(NOPOLY); SaveTaggedPoly(NOPOLY);
} }
@ -522,7 +664,7 @@ void TagProcess(CORO_PARAM, const void *) {
SaveTaggedPoly(NOPOLY); // No tagged polygon yet SaveTaggedPoly(NOPOLY); // No tagged polygon yet
while (1) { while (1) {
if (TagsActive == TAGS_ON) { if (bTagsActive) {
int curX, curY; // cursor position int curX, curY; // cursor position
while (!GetCursorXYNoWait(&curX, &curY, true)) while (!GetCursorXYNoWait(&curX, &curY, true))
CORO_SLEEP(1); CORO_SLEEP(1);
@ -533,6 +675,10 @@ void TagProcess(CORO_PARAM, const void *) {
if (_ctx->pText) { if (_ctx->pText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText); MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL; _ctx->pText = NULL;
if (TinselV2)
// May have buggered cursor
EndCursorFollowed();
} }
} }
} else { } else {
@ -557,22 +703,43 @@ void TagProcess(CORO_PARAM, const void *) {
/** /**
* Called from PointProcess() as appropriate. * Called from PointProcess() as appropriate.
*/ */
static void enteringpoly(HPOLYGON hp) { static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
SetPolyPointState(hp, POINTING); CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
RunPolyTinselCode(hp, POINTED, BE_NONE, false); CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_POINTING);
if (TinselV2)
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, POINTED, 0, false, 0));
else
RunPolyTinselCode(hp, POINTED, PLR_NOEVENT, false);
CORO_END_CODE;
} }
/** /**
* Called from PointProcess() as appropriate. * Called from PointProcess() as appropriate.
*/ */
static void leavingpoly(HPOLYGON hp) { static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
SetPolyPointState(hp, NOT_POINTING); CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
if (PolyTagState(hp) == TAG_ON) { CORO_BEGIN_CODE(_ctx);
SetPolyPointState(hp, PS_NOT_POINTING);
if (TinselV2) {
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, hp, UNPOINT, 0, false, 0));
SetPolyTagWanted(hp, false, false, 0);
} else if (PolyTagState(hp) == TAG_ON) {
// Delete this tag entry // Delete this tag entry
SetPolyTagState(hp, TAG_OFF); SetPolyTagState(hp, TAG_OFF);
} }
CORO_END_CODE;
} }
/** /**
@ -583,56 +750,95 @@ static void leavingpoly(HPOLYGON hp) {
void PointProcess(CORO_PARAM, const void *) { void PointProcess(CORO_PARAM, const void *) {
// COROUTINE // COROUTINE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
HPOLYGON hPoly;
int i;
int curX, curY; // cursor/tagged actor position
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
if (TinselV2)
EnablePointing();
while (1) { while (1) {
int aniX, aniY; // cursor/tagged actor position while (!GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
while (!GetCursorXYNoWait(&aniX, &aniY, true))
CORO_SLEEP(1); CORO_SLEEP(1);
/*----------------------------------*\ /*----------------------------------*\
| For polygons of type TAG and EXIT. | | For polygons of type TAG and EXIT. |
\*----------------------------------*/ \*----------------------------------*/
for (int i = 0; i < MAX_POLY; i++) { for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->i++) {
HPOLYGON hp = GetPolyHandle(i); _ctx->hPoly = GetPolyHandle(_ctx->i);
if ((_ctx->hPoly == NOPOLY) || ((PolyType(_ctx->hPoly) != TAG) &&
(PolyType(_ctx->hPoly) != EXIT)))
continue;
if (hp != NOPOLY && (PolyType(hp) == TAG || PolyType(hp) == EXIT)) { if (!PolyIsPointedTo(_ctx->hPoly)) {
if (PolyPointState(hp) == NOT_POINTING) { if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (IsInPolygon(aniX, aniY, hp)) { if (TinselV2) {
enteringpoly(hp); SetPolyPointedTo(_ctx->hPoly, true);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, POINTED, 0, false, 0));
} else {
CORO_INVOKE_1(enteringpoly, _ctx->hPoly);
} }
} else if (PolyPointState(hp) == POINTING) {
if (!IsInPolygon(aniX, aniY, hp)) {
leavingpoly(hp);
} }
} else {
if (!IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselV2) {
SetPolyPointedTo(_ctx->hPoly, false);
SetPolyTagWanted(_ctx->hPoly, false, false, 0);
CORO_INVOKE_ARGS(PolygonEvent, (CORO_SUBCTX, _ctx->hPoly, UNPOINT, 0, false, 0));
} else {
CORO_INVOKE_1(leavingpoly, _ctx->hPoly);
}
}
}
}
if (TinselV2) {
// For each tagged actor
for (_ctx->i = 0; (_ctx->i = NextTaggedActor(_ctx->i)) != 0; ) {
if (!ActorIsPointedTo(_ctx->i)) {
if (InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
SetActorPointedTo(_ctx->i, true);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, POINTED, false, 0));
}
} else {
if (!InHotSpot(_ctx->i, _ctx->curX, _ctx->curY)) {
SetActorPointedTo(_ctx->i, false);
SetActorTagWanted(_ctx->i, false, false, 0);
CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, _ctx->i, UNPOINT, false, 0));
} }
} }
} }
// allow re-scheduling // allow re-scheduling
do {
CORO_SLEEP(1); CORO_SLEEP(1);
} while (!bPointingActive);
} else {
// allow re-scheduling
CORO_SLEEP(1);
}
} }
CORO_END_CODE; CORO_END_CODE;
} }
void DisableTags(void) { void DisableTags(void) {
TagsActive = TAGS_OFF; bTagsActive = false;
} }
void EnableTags(void) { void EnableTags(void) {
TagsActive = TAGS_ON; bTagsActive = true;
} }
bool DisableTagsIfEnabled(void) { bool DisableTagsIfEnabled(void) {
if (TagsActive == TAGS_OFF) if (bTagsActive) {
return false; DisableTags();
else {
TagsActive = TAGS_OFF;
return true; return true;
} } else
return false;
} }
/** /**

37
engines/tinsel/pdisplay.h Normal file
View file

@ -0,0 +1,37 @@
/* 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$
*
* Tag related methods
*/
#ifndef TINSEL_PDISPLAY_H // prevent multiple includes
#define TINSEL_PDISPLAY_H
namespace Tinsel {
void EnableTags(void);
void DisableTags(void);
} // end of namespace Tinsel
#endif /* TINSEL_PDISPLAY_H */

View file

@ -61,12 +61,20 @@ namespace Tinsel {
#define PID_MASTER_SCR 0x00C0 // tinsel master script process #define PID_MASTER_SCR 0x00C0 // tinsel master script process
#define PID_MACTOR (0x00D0 | PID_DESTROY) // moving actor process #define PID_MOVER (0x00D0 | PID_DESTROY) // moving actor process
#define PID_REEL (0x00E0 | PID_DESTROY) // process for each film reel #define PID_REEL (0x00E0 | PID_DESTROY) // process for each film reel
#define PID_MIDI (0x00F0 | PID_DESTROY) // process to poll MIDI sound driver #define PID_MIDI (0x00F0 | PID_DESTROY) // process to poll MIDI sound driver
#define PID_BMV 0x0100 // Movie player process
#define PID_BTN_CLICK 0x110 // process to handle mouse button clicks
#define PID_PROCESS (0x0110 | PID_DESTROY) // Scene process base
#define PID_GPROCESS 0x0120 // Global process base
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_PID_H #endif // TINSEL_PID_H

File diff suppressed because it is too large Load diff

64
engines/tinsel/play.h Normal file
View file

@ -0,0 +1,64 @@
/* 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$
*
* Plays films within a scene, takes into account the actor in each 'column'.
*/
#ifndef TINSEL_PLAY_H // prevent multiple includes
#define TINSEL_PLAY_H
#include "tinsel/coroutine.h"
#include "tinsel/dw.h"
#include "tinsel/multiobj.h"
namespace Tinsel {
#define MAX_SOUNDREELS 5
struct SOUNDREELS {
SCNHANDLE hFilm; // The 'film'
int column; // Column number
int actorCol;
};
typedef SOUNDREELS *PSOUNDREELS;
void PlayFilm(CORO_PARAM, SCNHANDLE film, int x, int y, int actorid, bool splay, bool sfact, bool escOn,
int myescEvent, bool bTop);
void PlayFilm(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int myescEvent, bool bTop);
void PlayFilmc(CORO_PARAM, SCNHANDLE hFilm, int x, int y, int actorid, bool splay, bool sfact,
bool escOn, int myescEvent, bool bTop);
void RestoreActorReels(SCNHANDLE hFilm, short reelnum, short z, int x, int y);
void RestoreActorReels(SCNHANDLE hFilm, int actor, int x, int y);
void PokeInPalette(const MULTI_INIT *pmi);
void NoSoundReels(void);
void SaveSoundReels(PSOUNDREELS psr);
void RestoreSoundReels(PSOUNDREELS psr);
} // end of namespace Tinsel
#endif

File diff suppressed because it is too large Load diff

View file

@ -35,8 +35,12 @@ namespace Tinsel {
// Polygon Types // Polygon Types
enum PTYPE { enum PTYPE {
TEST, PATH, EXIT, BLOCKING, // Tinsel 2 Polygon type list
EFFECT, REFER, TAG, EX_TAG, EX_EXIT, EX_BLOCK TEST,
BLOCK, EFFECT, PATH, REFER, TAG,
EX_BLOCK, EX_EFFECT, EX_PATH, EX_REFER, EX_TAG,
// Extra polygon types from Tinsel v1
EXIT, EX_EXIT
}; };
// subtype // subtype
@ -45,6 +49,13 @@ enum {
NODE = 1 // For paths NODE = 1 // For paths
}; };
// tagFlags
enum {
POINTING = 0x01,
TAGWANTED = 0x02,
FOLLOWCURSOR = 0x04
};
// tagState // tagState
enum TSTATE { enum TSTATE {
TAG_OFF, TAG_ON TAG_OFF, TAG_ON
@ -52,7 +63,7 @@ enum TSTATE {
// pointState // pointState
enum PSTATE { enum PSTATE {
NO_POINT, NOT_POINTING, POINTING PS_NO_POINT, PS_NOT_POINTING, PS_POINTING
}; };
@ -60,7 +71,10 @@ enum {
NOPOLY = -1 NOPOLY = -1
}; };
struct POLY_VOLATILE {
bool bDead;
short xoff, yoff; // Polygon offset
};
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -69,26 +83,36 @@ HPOLYGON InPolygon(int xt, int yt, PTYPE type);
void BlockingCorner(HPOLYGON poly, int *x, int *y, int tarx, int tary); void BlockingCorner(HPOLYGON poly, int *x, int *y, int tarx, int tary);
void FindBestPoint(HPOLYGON path, int *x, int *y, int *line); void FindBestPoint(HPOLYGON path, int *x, int *y, int *line);
bool IsAdjacentPath(HPOLYGON path1, HPOLYGON path2); bool IsAdjacentPath(HPOLYGON path1, HPOLYGON path2);
HPOLYGON getPathOnTheWay(HPOLYGON from, HPOLYGON to); HPOLYGON GetPathOnTheWay(HPOLYGON from, HPOLYGON to);
int NearestEndNode(HPOLYGON path, int x, int y); int NearestEndNode(HPOLYGON path, int x, int y);
int NearEndNode(HPOLYGON spath, HPOLYGON dpath); int NearEndNode(HPOLYGON spath, HPOLYGON dpath);
int NearestNodeWithin(HPOLYGON npath, int x, int y); int NearestNodeWithin(HPOLYGON npath, int x, int y);
void NearestCorner(int *x, int *y, HPOLYGON spath, HPOLYGON dpath); void NearestCorner(int *x, int *y, HPOLYGON spath, HPOLYGON dpath);
bool IsPolyCorner(HPOLYGON hPath, int x, int y); bool IsPolyCorner(HPOLYGON hPath, int x, int y);
int GetScale(HPOLYGON path, int y); int GetScale(HPOLYGON path, int y);
int GetBrightness(HPOLYGON hPath, int y);
void getNpathNode(HPOLYGON npath, int node, int *px, int *py); void getNpathNode(HPOLYGON npath, int node, int *px, int *py);
void getPolyTagInfo(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy); void GetTagTag(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy);
SCNHANDLE getPolyFilm(HPOLYGON p); SCNHANDLE GetPolyFilm(HPOLYGON p);
void getPolyNode(HPOLYGON p, int *px, int *py); void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY);
SCNHANDLE getPolyScript(HPOLYGON p); SCNHANDLE GetPolyScript(HPOLYGON p);
REEL getPolyReelType(HPOLYGON p); REEL GetPolyReelType(HPOLYGON p);
int32 getPolyZfactor(HPOLYGON p); int32 GetPolyZfactor(HPOLYGON p);
int numNodes(HPOLYGON pp); int numNodes(HPOLYGON pp);
void RebootDeadTags(void); void RebootDeadTags(void);
void DisableBlock(int block);
void EnableBlock(int block);
void DisableEffect(int effect);
void EnableEffect(int effect);
void DisablePath(int path);
void EnablePath(int path);
void DisableRefer(int refer);
void EnableRefer(int refer);
void DisableBlock(int blockno); void DisableBlock(int blockno);
void EnableBlock(int blockno); void EnableBlock(int blockno);
void DisableTag(int tagno); HPOLYGON GetTagHandle(int tagno);
void EnableTag(int tagno); void DisableTag(CORO_PARAM, int tag);
void EnableTag(CORO_PARAM, int tag);
void DisableExit(int exitno); void DisableExit(int exitno);
void EnableExit(int exitno); void EnableExit(int exitno);
HPOLYGON FirstPathPoly(void); HPOLYGON FirstPathPoly(void);
@ -99,6 +123,8 @@ void DropPolygons(void);
void SaveDeadPolys(bool *sdp); void SaveDeadPolys(bool *sdp);
void RestoreDeadPolys(bool *sdp); void RestoreDeadPolys(bool *sdp);
void SavePolygonStuff(POLY_VOLATILE *sps);
void RestorePolygonStuff(POLY_VOLATILE *sps);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
@ -110,7 +136,6 @@ int PolyCornerX(HPOLYGON hp, int n); // ->cx[n]
int PolyCornerY(HPOLYGON hp, int n); // ->cy[n] int PolyCornerY(HPOLYGON hp, int n); // ->cy[n]
PSTATE PolyPointState(HPOLYGON hp); // ->pointState PSTATE PolyPointState(HPOLYGON hp); // ->pointState
TSTATE PolyTagState(HPOLYGON hp); // ->tagState TSTATE PolyTagState(HPOLYGON hp); // ->tagState
SCNHANDLE PolyTagHandle(HPOLYGON hp); // ->oTagHandle
void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState
void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState
@ -118,6 +143,21 @@ void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th);// ->oTagHandle
void MaxPolygons(int maxPolys); void MaxPolygons(int maxPolys);
int GetTagPolyId(HPOLYGON hp);
void GetTagTag(HPOLYGON hp, SCNHANDLE *hTagText, int *tagX, int *tagY);
void SetPolyPointedTo(HPOLYGON hp, bool bPointedTo);
bool PolyIsPointedTo(HPOLYGON hp);
void SetPolyTagWanted(HPOLYGON hp, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
bool PolyTagIsWanted(HPOLYGON hp);
bool PolyTagFollowsCursor(HPOLYGON hp);
SCNHANDLE GetPolyTagHandle(HPOLYGON hp);
bool IsTagPolygon(int tagno);
int GetTagPolyId(HPOLYGON hp);
void GetPolyMidBottom( HPOLYGON hp, int *pX, int *pY);
int PathCount(void);
void MovePolygon(PTYPE ptype, int id, int x, int y);
void MovePolygonTo(PTYPE ptype, int id, int x, int y);
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
} // end of namespace Tinsel } // end of namespace Tinsel

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@
#include "tinsel/anim.h" // for ANIM #include "tinsel/anim.h" // for ANIM
#include "tinsel/scene.h" // for TFTYPE #include "tinsel/scene.h" // for TFTYPE
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -39,31 +40,30 @@ enum NPS {NOT_IN, GOING_UP, GOING_DOWN, LEAVING, ENTERING};
enum IND {NO_PROB, TRY_CENTRE, TRY_CORNER, TRY_NEXTCORNER}; enum IND {NO_PROB, TRY_CENTRE, TRY_CORNER, TRY_NEXTCORNER};
enum MAS {NO_MACTOR, NORM_MACTOR}; enum DIRECTION { LEFTREEL, RIGHTREEL, FORWARD, AWAY };
enum DIRREEL{ LEFTREEL, RIGHTREEL, FORWARD, AWAY }; #define NUM_MAINSCALES (TinselV2 ? 10 : 5)
#define NUM_AUXSCALES 5
#define TOTAL_SCALES (NUM_MAINSCALES + NUM_AUXSCALES)
#define REQ_MAIN_SCALES 10
#define REQ_TOTAL_SCALES 15
enum { #define BOGUS_BRIGHTNESS -1
NUM_MAINSCALES = 5,
NUM_AUXSCALES = 5,
TOTAL_SCALES = NUM_MAINSCALES + NUM_AUXSCALES
};
struct MACTOR { struct MOVER {
int objx; /* Co-ordinates object */ int objX, objY; /* Co-ordinates object */
int objy;
int targetX, targetY; int targetX, targetY;
int ItargetX, ItargetY; /* Intermediate destination */ int ItargetX, ItargetY; /* Intermediate destination */
HPOLYGON hIpath;
int UtargetX, UtargetY; /* Ultimate destination */ int UtargetX, UtargetY; /* Ultimate destination */
HPOLYGON hUpath;
HPOLYGON hIpath;
HPOLYGON hUpath;
HPOLYGON hCpath; HPOLYGON hCpath;
bool over; bool over;
int ticket; int walkNumber;
IND InDifficulty; IND InDifficulty;
@ -74,129 +74,147 @@ struct MACTOR {
int Tline; // NEW int Tline; // NEW
bool TagReelRunning; // TODO: TagReelRunning may be the same as bSpecReel
bool bSpecReel;
/* Used internally */ /* Used internally */
DIRREEL dirn; // Current reel DIRECTION direction; // Current reel
int scale; // Current scale int scale; // Current scale
int scount; // Step count for walking reel synchronisation
unsigned fromx; int stepCount; // Step count for walking reel synchronisation
unsigned fromy;
int walkedFromX, walkedFromY;
bool bMoving; // Set this to TRUE during a walk bool bMoving; // Set this to TRUE during a walk
bool bNoPath; bool bNoPath;
bool bIgPath; bool bIgPath;
bool walkReel; bool bWalkReel;
OBJECT *actorObj; // Actor's object OBJECT *actorObj; // Actor's object
ANIM actorAnim; // Actor's animation script ANIM actorAnim; // Actor's animation script
SCNHANDLE lastfilm; // } Used by AlterActor() SCNHANDLE hLastFilm; // } Used by AlterMover()
SCNHANDLE pushedfilm; // } SCNHANDLE hPushedFilm; // }
int actorID; int actorID;
int actorToken; int actorToken;
SCNHANDLE WalkReels[TOTAL_SCALES][4]; SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE StandReels[TOTAL_SCALES][4]; SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
SCNHANDLE TalkReels[TOTAL_SCALES][4]; SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
MAS MActorState; bool bActive;
bool aHidden;
int SlowFactor; // Slow down movement while hidden int SlowFactor; // Slow down movement while hidden
bool stop; bool bStop;
/* NOTE: If effect polys can overlap, this needs improving */ /* NOTE: If effect polys can overlap, this needs improving */
bool InEffect; bool bInEffect;
PROCESS *pProc; PROCESS *pProc;
// Discworld 2 specific fields
int32 zOverride;
bool bHidden;
int brightness; // Current brightness
int startColour;
int paletteLength;
HPOLYGON hRpath; // Recent path
}; };
typedef MACTOR *PMACTOR; typedef MOVER *PMOVER;
struct MAINIT {
int X;
int Y;
PMOVER pMover;
};
typedef MAINIT *PMAINIT;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void MActorProcessCreate(int X, int Y, int id, MACTOR *pActor); void MoverProcessCreate(int X, int Y, int id, PMOVER pMover);
enum AR_FUNCTION { AR_NORMAL, AR_PUSHREEL, AR_POPREEL, AR_WALKREEL }; enum AR_FUNCTION { AR_NORMAL, AR_PUSHREEL, AR_POPREEL, AR_WALKREEL };
void StoreMoverPalette(PMOVER pMover, int startColour, int length);
MACTOR *GetMover(int ano); void MoverBrightness(PMOVER pMover, int brightness);
MACTOR *SetMover(int ano);
void KillMActor(MACTOR *pActor);
MACTOR *GetLiveMover(int index);
MAS getMActorState(MACTOR *psActor); MOVER *GetMover(int ano);
MOVER *RegisterMover(int ano);
void KillMover(PMOVER pMover);
MOVER *GetLiveMover(int index);
void hideMActor(MACTOR *pActor, int sf); bool getMActorState(MOVER *psActor);
bool getMActorHideState(MACTOR *pActor); int GetMoverId(PMOVER pMover);
void unhideMActor(MACTOR *pActor); void SetMoverZ(PMOVER pMover, int y, uint32 zFactor);
void DropMActors(void); void SetMoverZoverride(PMOVER pMover, uint32 zFactor);
void MoveMActor(MACTOR *pActor, int x, int y);
void GetMActorPosition(MACTOR *pActor, int *aniX, int *aniY); void HideMover(PMOVER pMover, int sf = 0);
void GetMActorMidTopPosition(MACTOR *pActor, int *aniX, int *aniY); bool MoverHidden(PMOVER pMover);
int GetMActorLeft(MACTOR *pActor); bool MoverIs(PMOVER pMover);
int GetMActorRight(MACTOR *pActor); bool MoverIsSWalking(PMOVER pMover);
bool MoverMoving(PMOVER pMover);
int GetWalkNumber(PMOVER pMover);
void UnHideMover(PMOVER pMover);
void DropMovers(void);
void PositionMover(PMOVER pMover, int x, int y);
bool MActorIsInPolygon(MACTOR *pActor, HPOLYGON hPoly); void GetMoverPosition(PMOVER pMover, int *aniX, int *aniY);
void AlterMActor(MACTOR *actor, SCNHANDLE film, AR_FUNCTION fn); void GetMoverMidTop(PMOVER pMover, int *aniX, int *aniY);
DIRREEL GetMActorDirection(MACTOR *pActor); int GetMoverLeft(PMOVER pMover);
int GetMActorScale(MACTOR *pActor); int GetMoverRight(PMOVER pMover);
void SetMActorDirection(MACTOR *pActor, DIRREEL dirn); int GetMoverTop(PMOVER pMover);
void SetMActorStanding(MACTOR *actor); int GetMoverBottom(PMOVER pMover);
void SetMActorWalkReel(MACTOR *actor, DIRREEL reel, int scale, bool force);
MACTOR *InMActorBlock(MACTOR *pActor, int x, int y); bool MoverIsInPolygon(PMOVER pMover, HPOLYGON hPoly);
void AlterMover(PMOVER pMover, SCNHANDLE film, AR_FUNCTION fn);
DIRECTION GetMoverDirection(PMOVER pMover);
int GetMoverScale(PMOVER pMover);
void SetMoverDirection(PMOVER pMover, DIRECTION dirn);
void SetMoverStanding(PMOVER pMover);
void SetMoverWalkReel(PMOVER pMover, DIRECTION reel, int scale, bool force);
PMOVER InMoverBlock(PMOVER pMover, int x, int y);
void RebootMovers(void); void RebootMovers(void);
bool IsMAinEffectPoly(int index); bool IsMAinEffectPoly(int index);
void SetMAinEffectPoly(int index, bool tf); void SetMoverInEffect(int index, bool tf);
bool MAmoving(MACTOR *pActor); void StopMover(PMOVER pMover);
int GetActorTicket(MACTOR *pActor);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
struct SAVED_MOVER { struct SAVED_MOVER {
MAS MActorState;
int actorID; int actorID;
int objx; int objX;
int objy; int objY;
SCNHANDLE lastfilm; SCNHANDLE hLastfilm;
SCNHANDLE WalkReels[TOTAL_SCALES][4]; SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE StandReels[TOTAL_SCALES][4]; SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
SCNHANDLE TalkReels[TOTAL_SCALES][4]; SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
bool bActive;
bool bHidden;
int brightness;
int startColour;
int paletteLength;
}; };
void SaveMovers(SAVED_MOVER *sMoverInfo); void SaveMovers(SAVED_MOVER *sMoverInfo);
void RestoreAuxScales(SAVED_MOVER *sMoverInfo); void RestoreAuxScales(SAVED_MOVER *sMoverInfo);
PMOVER NextMover(PMOVER pMover);
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/*
* Dodgy bit...
* These functions are now in MAREELS.C, but I can't be bothered to
* create a new header file.
*/
SCNHANDLE GetMactorTalkReel(MACTOR *pAactor, TFTYPE dirn);
void setscalingreels(int actor, int scale, int direction,
SCNHANDLE left, SCNHANDLE right, SCNHANDLE forward, SCNHANDLE away);
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRREEL reel);
void RebootScalingReels(void);
enum { enum {
MAGICX = -101, MAGICX = -101,
MAGICY = -102 MAGICY = -102

View file

@ -25,8 +25,9 @@
*/ */
#include "tinsel/actors.h" #include "tinsel/actors.h"
#include "tinsel/dialogs.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/inventory.h"
#include "tinsel/rince.h" #include "tinsel/rince.h"
#include "tinsel/savescn.h" #include "tinsel/savescn.h"
#include "tinsel/serializer.h" #include "tinsel/serializer.h"
@ -63,7 +64,11 @@ namespace Tinsel {
#define VER(x) x #define VER(x) x
//----------------- GLOBAL GLOBAL DATA --------------------
int thingHeld = 0;
int restoreCD = 0;
SRSTATE SRstate = SR_IDLE;
//----------------- EXTERN FUNCTIONS -------------------- //----------------- EXTERN FUNCTIONS --------------------
@ -79,9 +84,6 @@ extern void syncGlobInfo(Serializer &s);
// in POLYGONS.C // in POLYGONS.C
extern void syncPolyInfo(Serializer &s); extern void syncPolyInfo(Serializer &s);
// in SAVESCN.CPP
extern void RestoreScene(SAVED_DATA *sd, bool bFadeOut);
//----------------- LOCAL DEFINES -------------------- //----------------- LOCAL DEFINES --------------------
struct SaveGameHeader { struct SaveGameHeader {
@ -93,15 +95,17 @@ struct SaveGameHeader {
}; };
enum { enum {
SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld ScummVM" DW1_SAVEGAME_ID = 0x44575399, // = 'DWSc' = "DiscWorld 1 ScummVM"
DW2_SAVEGAME_ID = 0x44573253, // = 'DW2S' = "DiscWOrld 2 ScummVM"
SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7 SAVEGAME_HEADER_SIZE = 4 + 4 + 4 + SG_DESC_LEN + 7
}; };
#define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
static int numSfiles = 0; static int numSfiles = 0;
static SFILES savedFiles[MAX_SFILES]; static SFILES savedFiles[MAX_SAVED_FILES];
static bool NeedLoad = true; static bool NeedLoad = true;
@ -112,8 +116,6 @@ static const char *SaveSceneDesc = 0;
static int *SaveSceneSsCount = 0; static int *SaveSceneSsCount = 0;
static char *SaveSceneSsData = 0; // points to 'SAVED_DATA ssdata[MAX_NEST]' static char *SaveSceneSsData = 0; // points to 'SAVED_DATA ssdata[MAX_NEST]'
static SRSTATE SRstate = SR_IDLE;
//------------- SAVE/LOAD SUPPORT METHODS ---------------- //------------- SAVE/LOAD SUPPORT METHODS ----------------
static void syncTime(Serializer &s, struct tm &t) { static void syncTime(Serializer &s, struct tm &t) {
@ -153,29 +155,38 @@ static bool syncSaveGameHeader(Serializer &s, SaveGameHeader &hdr) {
} }
static void syncSavedMover(Serializer &s, SAVED_MOVER &sm) { static void syncSavedMover(Serializer &s, SAVED_MOVER &sm) {
SCNHANDLE *pList[3] = { (SCNHANDLE *)&sm.WalkReels, (SCNHANDLE *)&sm.StandReels, (SCNHANDLE *)&sm.TalkReels }; SCNHANDLE *pList[3] = { (SCNHANDLE *)&sm.walkReels,
(SCNHANDLE *)&sm.standReels, (SCNHANDLE *)&sm.talkReels };
s.syncAsUint32LE(sm.MActorState); s.syncAsUint32LE(sm.bActive);
s.syncAsSint32LE(sm.actorID); s.syncAsSint32LE(sm.actorID);
s.syncAsSint32LE(sm.objx); s.syncAsSint32LE(sm.objX);
s.syncAsSint32LE(sm.objy); s.syncAsSint32LE(sm.objY);
s.syncAsUint32LE(sm.lastfilm); s.syncAsUint32LE(sm.hLastfilm);
for (int pIndex = 0; pIndex < 3; ++pIndex) { for (int pIndex = 0; pIndex < 3; ++pIndex) {
SCNHANDLE *p = pList[pIndex]; SCNHANDLE *p = pList[pIndex];
for (int i = 0; i < TOTAL_SCALES * 4; ++i) for (int i = 0; i < TOTAL_SCALES * 4; ++i)
s.syncAsUint32LE(*p++); s.syncAsUint32LE(*p++);
} }
if (TinselV2) {
s.syncAsByte(sm.bHidden);
s.syncAsSint32LE(sm.brightness);
s.syncAsSint32LE(sm.startColour);
s.syncAsSint32LE(sm.paletteLength);
}
} }
static void syncSavedActor(Serializer &s, SAVED_ACTOR &sa) { static void syncSavedActor(Serializer &s, SAVED_ACTOR &sa) {
s.syncAsUint16LE(sa.actorID); s.syncAsUint16LE(sa.actorID);
s.syncAsUint16LE(sa.z); s.syncAsUint16LE(sa.zFactor);
s.syncAsUint32LE(sa.bAlive); s.syncAsUint32LE(sa.bAlive);
s.syncAsUint32LE(sa.presFilm); s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum); s.syncAsUint16LE(sa.presRnum);
s.syncAsUint16LE(sa.presX); s.syncAsUint16LE(sa.presPlayX);
s.syncAsUint16LE(sa.presY); s.syncAsUint16LE(sa.presPlayY);
} }
extern void syncAllActorsAlive(Serializer &s); extern void syncAllActorsAlive(Serializer &s);
@ -186,6 +197,24 @@ static void syncNoScrollB(Serializer &s, NOSCROLLB &ns) {
s.syncAsSint32LE(ns.c2); s.syncAsSint32LE(ns.c2);
} }
static void syncZPosition(Serializer &s, Z_POSITIONS &zp) {
s.syncAsSint16LE(zp.actor);
s.syncAsSint16LE(zp.column);
s.syncAsSint32LE(zp.z);
}
static void syncPolyVolatile(Serializer &s, POLY_VOLATILE &p) {
s.syncAsByte(p.bDead);
s.syncAsSint16LE(p.xoff);
s.syncAsSint16LE(p.yoff);
}
static void syncSoundReel(Serializer &s, SOUNDREELS &sr) {
s.syncAsUint32LE(sr.hFilm);
s.syncAsSint32LE(sr.column);
s.syncAsSint32LE(sr.actorCol);
}
static void syncSavedData(Serializer &s, SAVED_DATA &sd) { static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
s.syncAsUint32LE(sd.SavedSceneHandle); s.syncAsUint32LE(sd.SavedSceneHandle);
s.syncAsUint32LE(sd.SavedBgroundHandle); s.syncAsUint32LE(sd.SavedBgroundHandle);
@ -197,7 +226,7 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
s.syncAsSint32LE(sd.NumSavedActors); s.syncAsSint32LE(sd.NumSavedActors);
s.syncAsSint32LE(sd.SavedLoffset); s.syncAsSint32LE(sd.SavedLoffset);
s.syncAsSint32LE(sd.SavedToffset); s.syncAsSint32LE(sd.SavedToffset);
for (int i = 0; i < MAX_INTERPRET; ++i) for (int i = 0; i < NUM_INTERPRET; ++i)
sd.SavedICInfo[i].syncWithSerializer(s); sd.SavedICInfo[i].syncWithSerializer(s);
for (int i = 0; i < MAX_POLY; ++i) for (int i = 0; i < MAX_POLY; ++i)
s.syncAsUint32LE(sd.SavedDeadPolys[i]); s.syncAsUint32LE(sd.SavedDeadPolys[i]);
@ -213,6 +242,32 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]); syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV); s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH); s.syncAsUint32LE(sd.SavedNoScrollData.NumNoH);
// Tinsel 2 fields
if (TinselV2) {
// SavedNoScrollData
s.syncAsUint32LE(sd.SavedNoScrollData.xTrigger);
s.syncAsUint32LE(sd.SavedNoScrollData.xDistance);
s.syncAsUint32LE(sd.SavedNoScrollData.xSpeed);
s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerTop);
s.syncAsUint32LE(sd.SavedNoScrollData.yTriggerBottom);
s.syncAsUint32LE(sd.SavedNoScrollData.yDistance);
s.syncAsUint32LE(sd.SavedNoScrollData.ySpeed);
for (int i = 0; i < NUM_ZPOSITIONS; ++i)
syncZPosition(s, sd.zPositions[i]);
s.syncBytes(sd.savedActorZ, MAX_SAVED_ACTOR_Z);
for (int i = 0; i < MAX_POLY; ++i)
syncPolyVolatile(s, sd.SavedPolygonStuff[i]);
for (int i = 0; i < 3; ++i)
s.syncAsUint32LE(sd.SavedTune[i]);
s.syncAsByte(sd.bTinselDim);
s.syncAsSint32LE(sd.SavedScrollFocus);
for (int i = 0; i < SV_TOPVALID; ++i)
s.syncAsSint32LE(sd.SavedSystemVars[i]);
for (int i = 0; i < MAX_SOUNDREELS; ++i)
syncSoundReel(s, sd.SavedSoundReels[i]);
}
} }
@ -247,6 +302,11 @@ static char *NewName(void) {
* the number of files found). * the number of files found).
*/ */
int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) { int getList(Common::SaveFileManager *saveFileMan, const Common::String &target) {
// No change since last call?
// TODO/FIXME: Just always reload this data? Be careful about slow downs!!!
if (!NeedLoad)
return numSfiles;
int i; int i;
const Common::String pattern = target + ".???"; const Common::String pattern = target + ".???";
@ -255,7 +315,7 @@ int getList(Common::SaveFileManager *saveFileMan, const Common::String &target)
numSfiles = 0; numSfiles = 0;
for (Common::StringList::const_iterator file = files.begin(); file != files.end(); ++file) { for (Common::StringList::const_iterator file = files.begin(); file != files.end(); ++file) {
if (numSfiles >= MAX_SFILES) if (numSfiles >= MAX_SAVED_FILES)
break; break;
const Common::String &fname = *file; const Common::String &fname = *file;
@ -308,7 +368,6 @@ int getList(void) {
return getList(_vm->getSaveFileMan(), _vm->getTargetName()); return getList(_vm->getSaveFileMan(), _vm->getTargetName());
} }
char *ListEntry(int i, letype which) { char *ListEntry(int i, letype which) {
if (i == -1) if (i == -1)
i = numSfiles; i = numSfiles;
@ -322,7 +381,16 @@ char *ListEntry(int i, letype which) {
} }
static void DoSync(Serializer &s) { static void DoSync(Serializer &s) {
int sg = 0; int sg;
if (TinselV2) {
if (s.isSaving())
restoreCD = GetCurrentCD();
s.syncAsSint16LE(restoreCD);
}
if (TinselV2 && s.isLoading())
HoldItem(INV_NOICON);
syncSavedData(s, *srsd); syncSavedData(s, *srsd);
syncGlobInfo(s); // Glitter globals syncGlobInfo(s); // Glitter globals
@ -332,10 +400,15 @@ static void DoSync(Serializer &s) {
if (s.isSaving()) if (s.isSaving())
sg = WhichItemHeld(); sg = WhichItemHeld();
s.syncAsSint32LE(sg); s.syncAsSint32LE(sg);
if (s.isLoading()) if (s.isLoading()) {
if (TinselV2)
thingHeld = sg;
else
HoldItem(sg); HoldItem(sg);
}
syncTimerInfo(s); // Timer data syncTimerInfo(s); // Timer data
if (!TinselV2)
syncPolyInfo(s); // Dead polygon data syncPolyInfo(s); // Dead polygon data
syncSCdata(s); // Hook Scene and delayed scene syncSCdata(s); // Hook Scene and delayed scene
@ -347,6 +420,7 @@ static void DoSync(Serializer &s) {
syncSavedData(s, *sdPtr); syncSavedData(s, *sdPtr);
} }
if (!TinselV2)
syncAllActorsAlive(s); syncAllActorsAlive(s);
} }
@ -440,7 +514,7 @@ void ProcessSRQueue(void) {
switch (SRstate) { switch (SRstate) {
case SR_DORESTORE: case SR_DORESTORE:
if (DoRestore()) { if (DoRestore()) {
RestoreScene(srsd, false); DoRestoreScene(srsd, false);
} }
SRstate = SR_IDLE; SRstate = SR_IDLE;
break; break;
@ -467,6 +541,15 @@ void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *pSsCount, SAVE
} }
void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) { void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsData) {
if (TinselV2) {
if (num == -1)
return;
else if (num == -2) {
// From CD change for restore
num = RestoreGameNumber;
}
}
assert(num >= 0); assert(num >= 0);
RestoreGameNumber = num; RestoreGameNumber = num;
@ -476,4 +559,14 @@ void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsD
SRstate = SR_DORESTORE; SRstate = SR_DORESTORE;
} }
/**
* Returns the index of the most recently saved savegame. This will always be
* the file at the first index, since the list is sorted by date/time
*/
int NewestSavedGame(void) {
int numFiles = getList();
return (numFiles == 0) ? -1 : 0;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -28,19 +28,24 @@
#include "tinsel/actors.h" #include "tinsel/actors.h"
#include "tinsel/background.h" #include "tinsel/background.h"
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/faders.h" // FadeOutFast() #include "tinsel/faders.h" // FadeOutFast()
#include "tinsel/graphics.h" // ClearScreen() #include "tinsel/graphics.h" // ClearScreen()
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/inventory.h" #include "tinsel/heapmem.h"
#include "tinsel/dialogs.h"
#include "tinsel/music.h" #include "tinsel/music.h"
#include "tinsel/pid.h" #include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/polygons.h" #include "tinsel/polygons.h"
#include "tinsel/rince.h" #include "tinsel/rince.h"
#include "tinsel/savescn.h" #include "tinsel/savescn.h"
#include "tinsel/scene.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/scroll.h" #include "tinsel/scroll.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinlib.h" #include "tinsel/tinlib.h"
#include "tinsel/token.h" #include "tinsel/token.h"
@ -49,7 +54,6 @@ namespace Tinsel {
//----------------- EXTERN FUNCTIONS -------------------- //----------------- EXTERN FUNCTIONS --------------------
// in BG.C // in BG.C
extern void startupBackground(SCNHANDLE bfilm);
extern SCNHANDLE GetBgroundHandle(void); extern SCNHANDLE GetBgroundHandle(void);
extern void SetDoFadeIn(bool tf); extern void SetDoFadeIn(bool tf);
@ -59,14 +63,8 @@ void RestoreMasterProcess(INT_CONTEXT *pic);
// in EVENTS.C (declared here and not in events.h because of strange goings-on) // in EVENTS.C (declared here and not in events.h because of strange goings-on)
void RestoreProcess(INT_CONTEXT *pic); void RestoreProcess(INT_CONTEXT *pic);
// in PLAY.C
extern void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y);
// in SCENE.C // in SCENE.C
extern SCNHANDLE GetSceneHandle(void); extern SCNHANDLE GetSceneHandle(void);
extern void NewScene(SCNHANDLE scene, int entry);
//----------------- LOCAL DEFINES -------------------- //----------------- LOCAL DEFINES --------------------
@ -77,6 +75,11 @@ enum {
MAX_NEST = 4 MAX_NEST = 4
}; };
//----------------- EXTERNAL GLOBAL DATA --------------------
extern int thingHeld;
extern int restoreCD;
extern SRSTATE SRstate;
//----------------- LOCAL GLOBAL DATA -------------------- //----------------- LOCAL GLOBAL DATA --------------------
@ -84,54 +87,52 @@ static bool ASceneIsSaved = false;
static int savedSceneCount = 0; static int savedSceneCount = 0;
//static SAVED_DATA s_ssData[MAX_NEST]; static bool bNotDoneYet = false;
static SAVED_DATA *s_ssData = 0;
//static SAVED_DATA ssData[MAX_NEST];
static SAVED_DATA *ssData = NULL;
static SAVED_DATA sgData; static SAVED_DATA sgData;
static SAVED_DATA *s_rsd = 0; static SAVED_DATA *rsd = 0;
static int s_restoreSceneCount = 0; static int RestoreSceneCount = 0;
static bool bNoFade = false; static bool bNoFade = false;
//----------------- FORWARD REFERENCES -------------------- //----------------- FORWARD REFERENCES --------------------
void InitialiseSs(void) {
if (s_ssData == NULL) {
s_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
if (s_ssData == NULL) {
error("Cannot allocate memory for scene changes");
}
} else
savedSceneCount = 0;
}
void FreeSs(void) {
if (s_ssData) {
free(s_ssData);
s_ssData = NULL;
}
}
/** /**
* Save current scene. * Save current scene.
* @param sd Pointer to the scene data * @param sd Pointer to the scene data
*/ */
void SaveScene(SAVED_DATA *sd) { void DoSaveScene(SAVED_DATA *sd) {
sd->SavedSceneHandle = GetSceneHandle(); sd->SavedSceneHandle = GetSceneHandle();
sd->SavedBgroundHandle = GetBgroundHandle(); sd->SavedBgroundHandle = GetBgroundHandle();
SaveMovers(sd->SavedMoverInfo); SaveMovers(sd->SavedMoverInfo);
sd->NumSavedActors = SaveActors(sd->SavedActorInfo); sd->NumSavedActors = SaveActors(sd->SavedActorInfo);
PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset); PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
SaveInterpretContexts(sd->SavedICInfo); SaveInterpretContexts(sd->SavedICInfo);
SaveDeadPolys(sd->SavedDeadPolys); sd->SavedControl = ControlIsOn();
sd->SavedControl = TestToken(TOKEN_CONTROL); sd->SavedNoBlocking = GetNoBlocking();
CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
sd->SavedNoBlocking = bNoBlocking;
GetNoScrollData(&sd->SavedNoScrollData); GetNoScrollData(&sd->SavedNoScrollData);
if (TinselV2) {
// Tinsel 2 specific data save
SaveActorZ(sd->savedActorZ);
SaveZpositions(sd->zPositions);
SavePolygonStuff(sd->SavedPolygonStuff);
_vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
sd->SavedScrollFocus = GetScrollFocus();
SaveSysVars(sd->SavedSystemVars);
SaveSoundReels(sd->SavedSoundReels);
} else {
// Tinsel 1 specific data save
SaveDeadPolys(sd->SavedDeadPolys);
CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
}
ASceneIsSaved = true; ASceneIsSaved = true;
} }
@ -140,13 +141,32 @@ void SaveScene(SAVED_DATA *sd) {
* @param sd Pointer to the scene data * @param sd Pointer to the scene data
* @param bFadeOut Flag to perform a fade out * @param bFadeOut Flag to perform a fade out
*/ */
void RestoreScene(SAVED_DATA *sd, bool bFadeOut) { void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
s_rsd = sd; rsd = sd;
if (bFadeOut) if (bFadeOut)
s_restoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
else else
s_restoreSceneCount = RS_COUNT; // Set restore scene count RestoreSceneCount = RS_COUNT; // Set restore scene count
}
void InitialiseSaveScenes(void) {
if (ssData == NULL) {
ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
if (ssData == NULL) {
error("Cannot allocate memory for scene changes");
}
} else {
// Re-initialise - no scenes saved
savedSceneCount = 0;
}
}
void FreeSaveScenes(void) {
if (ssData) {
free(ssData);
ssData = NULL;
}
} }
/** /**
@ -154,39 +174,84 @@ void RestoreScene(SAVED_DATA *sd, bool bFadeOut) {
* the scene was saved. * the scene was saved.
* Also 'stand' all the moving actors at their saved positions. * Also 'stand' all the moving actors at their saved positions.
*/ */
void sortActors(SAVED_DATA *rsd) { void sortActors(SAVED_DATA *sd) {
for (int i = 0; i < rsd->NumSavedActors; i++) { assert(!TinselV2);
ActorsLife(rsd->SavedActorInfo[i].actorID, rsd->SavedActorInfo[i].bAlive); for (int i = 0; i < sd->NumSavedActors; i++) {
ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
// Should be playing the same reel. // Should be playing the same reel.
if (rsd->SavedActorInfo[i].presFilm != 0) { if (sd->SavedActorInfo[i].presFilm != 0) {
if (!actorAlive(rsd->SavedActorInfo[i].actorID)) if (!actorAlive(sd->SavedActorInfo[i].actorID))
continue; continue;
playThisReel(rsd->SavedActorInfo[i].presFilm, rsd->SavedActorInfo[i].presRnum, rsd->SavedActorInfo[i].z, RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
rsd->SavedActorInfo[i].presX, rsd->SavedActorInfo[i].presY); sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
} }
} }
RestoreAuxScales(rsd->SavedMoverInfo); RestoreAuxScales(sd->SavedMoverInfo);
for (int i = 0; i < MAX_MOVERS; i++) { for (int i = 0; i < MAX_MOVERS; i++) {
if (rsd->SavedMoverInfo[i].MActorState == NORM_MACTOR) if (sd->SavedMoverInfo[i].bActive)
stand(rsd->SavedMoverInfo[i].actorID, rsd->SavedMoverInfo[i].objx, Stand(nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
rsd->SavedMoverInfo[i].objy, rsd->SavedMoverInfo[i].lastfilm); sd->SavedMoverInfo[i].objY, sd->SavedMoverInfo[i].hLastfilm);
} }
} }
/**
* Stand all the moving actors at their saved positions.
* Not called from the foreground.
*/
static void SortMAProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CONTEXT;
int i;
int viaActor;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Disable via actor for the stands
_ctx->viaActor = SysVar(ISV_DIVERT_ACTOR);
SetSysVar(ISV_DIVERT_ACTOR, 0);
RestoreAuxScales(rsd->SavedMoverInfo);
for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
if (rsd->SavedMoverInfo[_ctx->i].bActive) {
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, rsd->SavedMoverInfo[_ctx->i].actorID,
rsd->SavedMoverInfo[_ctx->i].objX, rsd->SavedMoverInfo[_ctx->i].objY,
rsd->SavedMoverInfo[_ctx->i].hLastfilm));
if (rsd->SavedMoverInfo[_ctx->i].bHidden)
HideMover(GetMover(rsd->SavedMoverInfo[_ctx->i].actorID));
}
ActorPalette(rsd->SavedMoverInfo[_ctx->i].actorID,
rsd->SavedMoverInfo[_ctx->i].startColour, rsd->SavedMoverInfo[_ctx->i].paletteLength);
if (rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
ActorBrightness(rsd->SavedMoverInfo[_ctx->i].actorID, rsd->SavedMoverInfo[_ctx->i].brightness);
}
// Restore via actor
SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
bNotDoneYet = false;
CORO_END_CODE;
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void ResumeInterprets(SAVED_DATA *rsd) { void ResumeInterprets(void) {
// Master script only affected on restore game, not restore scene // Master script only affected on restore game, not restore scene
if (rsd == &sgData) { if (!TinselV2 && (rsd == &sgData)) {
g_scheduler->killMatchingProcess(PID_MASTER_SCR, -1); g_scheduler->killMatchingProcess(PID_MASTER_SCR, -1);
FreeMasterInterpretContext(); FreeMasterInterpretContext();
} }
for (int i = 0; i < MAX_INTERPRET; i++) { for (int i = 0; i < NUM_INTERPRET; i++) {
switch (rsd->SavedICInfo[i].GSort) { switch (rsd->SavedICInfo[i].GSort) {
case GS_NONE: case GS_NONE:
break; break;
@ -203,14 +268,31 @@ void ResumeInterprets(SAVED_DATA *rsd) {
RestoreMasterProcess(&rsd->SavedICInfo[i]); RestoreMasterProcess(&rsd->SavedICInfo[i]);
break; break;
case GS_PROCESS:
// Tinsel 2 process
RestoreSceneProcess(&rsd->SavedICInfo[i]);
break;
case GS_GPROCESS:
// Tinsel 2 Global processes only affected on restore game, not restore scene
if (rsd == &sgData)
RestoreGlobalProcess(&rsd->SavedICInfo[i]);
break;
case GS_ACTOR: case GS_ACTOR:
RestoreActorProcess(rsd->SavedICInfo[i].actorid, &rsd->SavedICInfo[i]); if (TinselV2)
RestoreProcess(&rsd->SavedICInfo[i]);
else
RestoreActorProcess(rsd->SavedICInfo[i].idActor, &rsd->SavedICInfo[i]);
break; break;
case GS_POLYGON: case GS_POLYGON:
case GS_SCENE: case GS_SCENE:
RestoreProcess(&rsd->SavedICInfo[i]); RestoreProcess(&rsd->SavedICInfo[i]);
break; break;
default:
warning("Unhandled GSort in ResumeInterprets");
} }
} }
} }
@ -219,7 +301,7 @@ void ResumeInterprets(SAVED_DATA *rsd) {
* Do restore scene * Do restore scene
* @param n Id * @param n Id
*/ */
static int DoRestoreScene(SAVED_DATA *rsd, int n) { static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
switch (n) { switch (n) {
case RS_COUNT + COUNTOUT_COUNT: case RS_COUNT + COUNTOUT_COUNT:
// Trigger pre-load and fade and start countdown // Trigger pre-load and fade and start countdown
@ -229,31 +311,90 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
case RS_COUNT: case RS_COUNT:
_vm->_sound->stopAllSamples(); _vm->_sound->stopAllSamples();
ClearScreen(); ClearScreen();
RestoreDeadPolys(rsd->SavedDeadPolys);
NewScene(rsd->SavedSceneHandle, NO_ENTRY_NUM); // Master script only affected on restore game, not restore scene
if (TinselV2 && (sd == &sgData)) {
g_scheduler->killMatchingProcess(PID_MASTER_SCR);
KillGlobalProcesses();
FreeMasterInterpretContext();
}
if (TinselV2) {
RestorePolygonStuff(sd->SavedPolygonStuff);
// Abandon temporarily if different CD
if (sd == &sgData && restoreCD != GetCurrentCD()) {
SRstate = SR_IDLE;
EndScene();
SetNextCD(restoreCD);
CDChangeForRestore(restoreCD);
return 0;
}
} else {
RestoreDeadPolys(sd->SavedDeadPolys);
}
// Start up the scene
StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
SetDoFadeIn(!bNoFade); SetDoFadeIn(!bNoFade);
bNoFade = false; bNoFade = false;
startupBackground(rsd->SavedBgroundHandle); StartupBackground(nullContext, sd->SavedBgroundHandle);
KillScroll();
PlayfieldSetPos(FIELD_WORLD, rsd->SavedLoffset, rsd->SavedToffset);
bNoBlocking = rsd->SavedNoBlocking;
RestoreNoScrollData(&rsd->SavedNoScrollData);
/*
break;
case RS_COUNT - 1: if (TinselV2) {
*/ Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
sortActors(rsd); } else {
KillScroll();
PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
SetNoBlocking(sd->SavedNoBlocking);
}
RestoreNoScrollData(&sd->SavedNoScrollData);
if (TinselV2) {
// create process to sort out the moving actors
g_scheduler->createProcess(PID_MOVER, SortMAProcess, NULL, 0);
bNotDoneYet = true;
RestoreActorZ(sd->savedActorZ);
RestoreZpositions(sd->zPositions);
RestoreSysVars(sd->SavedSystemVars);
CreateGhostPalette(BgPal());
RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
RestoreSoundReels(sd->SavedSoundReels);
return 1;
}
sortActors(sd);
break; break;
case 2: case 2:
break; break;
case 1: case 1:
RestoreMidiFacts(rsd->SavedMidi, rsd->SavedLoop); if (TinselV2) {
if (rsd->SavedControl) if (bNotDoneYet)
control(CONTROL_ON); // TOKEN_CONTROL was free return n;
ResumeInterprets(rsd);
if (sd == &sgData)
HoldItem(thingHeld, true);
if (sd->bTinselDim)
_vm->_pcmMusic->dim(true);
_vm->_pcmMusic->restoreThatTune(sd->SavedTune);
ScrollFocus(sd->SavedScrollFocus);
} else {
RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
}
if (sd->SavedControl)
ControlOn(); // Control was on
ResumeInterprets();
break;
default:
break;
} }
return n - 1; return n - 1;
@ -266,7 +407,7 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
void RestoreGame(int num) { void RestoreGame(int num) {
KillInventory(); KillInventory();
RequestRestoreGame(num, &sgData, &savedSceneCount, s_ssData); RequestRestoreGame(num, &sgData, &savedSceneCount, ssData);
// Actual restoring is performed by ProcessSRQueue // Actual restoring is performed by ProcessSRQueue
} }
@ -278,9 +419,9 @@ void RestoreGame(int num) {
*/ */
void SaveGame(char *name, char *desc) { void SaveGame(char *name, char *desc) {
// Get current scene data // Get current scene data
SaveScene(&sgData); DoSaveScene(&sgData);
RequestSaveGame(name, desc, &sgData, &savedSceneCount, s_ssData); RequestSaveGame(name, desc, &sgData, &savedSceneCount, ssData);
// Actual saving is performed by ProcessSRQueue // Actual saving is performed by ProcessSRQueue
} }
@ -289,23 +430,23 @@ void SaveGame(char *name, char *desc) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool IsRestoringScene() { bool IsRestoringScene() {
if (s_restoreSceneCount) { if (RestoreSceneCount) {
s_restoreSceneCount = DoRestoreScene(s_rsd, s_restoreSceneCount); RestoreSceneCount = DoRestoreSceneFrame(rsd, RestoreSceneCount);
} }
return s_restoreSceneCount ? true : false; return RestoreSceneCount ? true : false;
} }
/** /**
* Please Restore Scene * Restores Scene
*/ */
void PleaseRestoreScene(bool bFade) { void TinselRestoreScene(bool bFade) {
// only called by restore_scene PCODE // only called by restore_scene PCODE
if (s_restoreSceneCount == 0) { if (RestoreSceneCount == 0) {
assert(savedSceneCount >= 1); // No saved scene to restore assert(savedSceneCount >= 1); // No saved scene to restore
if (ASceneIsSaved) if (ASceneIsSaved)
RestoreScene(&s_ssData[--savedSceneCount], bFade); DoRestoreScene(&ssData[--savedSceneCount], bFade);
if (!bFade) if (!bFade)
bNoFade = true; bNoFade = true;
} }
@ -314,7 +455,7 @@ void PleaseRestoreScene(bool bFade) {
/** /**
* Please Save Scene * Please Save Scene
*/ */
void PleaseSaveScene(CORO_PARAM) { void TinselSaveScene(CORO_PARAM) {
// only called by save_scene PCODE // only called by save_scene PCODE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
@ -325,10 +466,10 @@ void PleaseSaveScene(CORO_PARAM) {
// Don't save the same thing multiple times! // Don't save the same thing multiple times!
// FIXME/TODO: Maybe this can be changed to an assert? // FIXME/TODO: Maybe this can be changed to an assert?
if (savedSceneCount && s_ssData[savedSceneCount-1].SavedSceneHandle == GetSceneHandle()) if (savedSceneCount && ssData[savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
CORO_KILL_SELF(); CORO_KILL_SELF();
SaveScene(&s_ssData[savedSceneCount++]); DoSaveScene(&ssData[savedSceneCount++]);
CORO_END_CODE; CORO_END_CODE;
} }

View file

@ -33,13 +33,16 @@
#include "tinsel/dw.h" // SCNHANDLE #include "tinsel/dw.h" // SCNHANDLE
#include "tinsel/rince.h" // SAVED_MOVER #include "tinsel/rince.h" // SAVED_MOVER
#include "tinsel/pcode.h" // INT_CONTEXT #include "tinsel/pcode.h" // INT_CONTEXT
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/scroll.h" // SCROLLDATA #include "tinsel/scroll.h" // SCROLLDATA
#include "tinsel/sysvar.h"
namespace Tinsel { namespace Tinsel {
enum { enum {
SG_DESC_LEN = 40, // Max. saved game description length SG_DESC_LEN = 40, // Max. saved game description length
MAX_SFILES = 30, MAX_SAVED_FILES = 30,
// FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the // FIXME: Save file names in ScummVM can be longer than 8.3, overflowing the
// name field in savedFiles. Raising it to 256 as a preliminary fix. // name field in savedFiles. Raising it to 256 as a preliminary fix.
@ -66,6 +69,16 @@ struct SAVED_DATA {
bool SavedLoop; // } Midi bool SavedLoop; // } Midi
bool SavedNoBlocking; bool SavedNoBlocking;
SCROLLDATA SavedNoScrollData; SCROLLDATA SavedNoScrollData;
// Tinsel 2 fields
Z_POSITIONS zPositions[NUM_ZPOSITIONS];
byte savedActorZ[MAX_SAVED_ACTOR_Z];
POLY_VOLATILE SavedPolygonStuff[MAX_POLY];
uint32 SavedTune[3]; // Music
bool bTinselDim;
int SavedScrollFocus;
int SavedSystemVars[SV_TOPVALID];
SOUNDREELS SavedSoundReels[MAX_SOUNDREELS];
}; };
@ -74,8 +87,10 @@ enum SRSTATE {
SR_DOSAVE, SR_DONESAVE, SR_ABORTED SR_DOSAVE, SR_DONESAVE, SR_ABORTED
}; };
void PleaseRestoreScene(bool bFade); void TinselRestoreScene(bool bFade);
void PleaseSaveScene(CORO_PARAM); void TinselSaveScene(CORO_PARAM);
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut);
void DoSaveScene(SAVED_DATA *sd);
bool IsRestoringScene(); bool IsRestoringScene();
@ -95,8 +110,8 @@ void ProcessSRQueue(void);
void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData); void RequestSaveGame(char *name, char *desc, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
void RequestRestoreGame(int num, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData); void RequestRestoreGame(int num, SAVED_DATA *sd, int *ssCount, SAVED_DATA *ssData);
void InitialiseSs(void); void InitialiseSaveScenes(void);
void FreeSs(void); void FreeSaveScenes(void);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -29,21 +29,26 @@
#include "tinsel/background.h" #include "tinsel/background.h"
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/cursor.h" #include "tinsel/cursor.h"
#include "tinsel/dialogs.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/graphics.h" #include "tinsel/graphics.h"
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/inventory.h"
#include "tinsel/film.h" #include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/mareels.h"
#include "tinsel/move.h" #include "tinsel/move.h"
#include "tinsel/music.h"
#include "tinsel/object.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h" // process IDs
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h" #include "tinsel/rince.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/scn.h" #include "tinsel/scn.h"
#include "tinsel/scroll.h" #include "tinsel/scroll.h"
#include "tinsel/sound.h" // stopAllSamples() #include "tinsel/sound.h" // stopAllSamples()
#include "tinsel/object.h" #include "tinsel/sysvar.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h" // process IDs
#include "tinsel/polygons.h"
#include "tinsel/token.h" #include "tinsel/token.h"
@ -72,20 +77,28 @@ extern void EnableTags(void);
/** scene structure - one per scene */ /** scene structure - one per scene */
struct SCENE_STRUC { struct SCENE_STRUC {
int32 numEntrance; //!< number of entrances in this scene int32 defRefer; // Default refer direction (REFTYPE)
int32 numPoly; //!< number of various polygons in this scene SCNHANDLE hSceneScript; // handle to scene script
int32 numActor; //!< number of actors in this scene SCNHANDLE hSceneDesc; // handle to scene description
int32 defRefer; //!< Default refer direction int32 numEntrance; // number of entrances in this scene
SCNHANDLE hSceneScript; //!< handle to scene script SCNHANDLE hEntrance; // handle to table of entrances
SCNHANDLE hEntrance; //!< handle to table of entrances int32 numPoly; // number of various polygons in this scene
SCNHANDLE hPoly; //!< handle to table of polygons SCNHANDLE hPoly; // handle to table of polygons
SCNHANDLE hActor; //!< handle to table of actors int32 numTaggedActor; // number of tagged actors in this scene
SCNHANDLE hTaggedActor; // handle to table of tagged actors
int32 numProcess; // number of processes in this scene
SCNHANDLE hProcess; // handle to table of processes
SCNHANDLE hMusicScript; // handle to music script data
SCNHANDLE hMusicSegment;// handle to music segments
} PACKED_STRUCT; } PACKED_STRUCT;
/** entrance structure - one per entrance */ /** entrance structure - one per entrance */
struct ENTRANCE_STRUC { struct ENTRANCE_STRUC {
int32 eNumber; //!< entrance number int32 eNumber; //!< entrance number
SCNHANDLE hScript; //!< handle to entrance script SCNHANDLE hScript; //!< handle to entrance script
// Tinsel 2 fields
SCNHANDLE hEntDesc; // handle to entrance description
uint32 flags;
} PACKED_STRUCT; } PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING #include "common/pack-end.h" // END STRUCT PACKING
@ -97,8 +110,39 @@ struct ENTRANCE_STRUC {
static bool ShowPosition = false; // Set when showpos() has been called static bool ShowPosition = false; // Set when showpos() has been called
#endif #endif
SCNHANDLE newestScene = 0;
static SCNHANDLE SceneHandle = 0; // Current scene handle - stored in case of Save_Scene() static SCNHANDLE SceneHandle = 0; // Current scene handle - stored in case of Save_Scene()
static bool bWatchingOut = false;
SCENE_STRUC tempStruc;
struct TP_INIT {
SCNHANDLE hTinselCode; // Code
TINSEL_EVENT event; // Triggering event
};
const SCENE_STRUC *GetSceneStruc(const byte *pStruc) {
if (TinselVersion == TINSEL_V2)
return (const SCENE_STRUC *)pStruc;
// Copy appropriate fields into tempStruc, and return a pointer to it
const uint32 *p = (const uint32 *)pStruc;
memset(&tempStruc, sizeof(SCENE_STRUC), 0);
tempStruc.numEntrance = *p++;
tempStruc.numPoly = *p++;
tempStruc.numTaggedActor = *p++;
tempStruc.defRefer = *p++;
tempStruc.hSceneScript = *p++;
tempStruc.hEntrance = *p++;
tempStruc.hPoly = *p++;
tempStruc.hTaggedActor = *p++;
return &tempStruc;
}
/** /**
* Started up for scene script and entrance script. * Started up for scene script and entrance script.
@ -107,20 +151,59 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE // COROUTINE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic; INT_CONTEXT *pic;
const TP_INIT *pInit;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
// get the stuff copied to process when it was created
SCNHANDLE *ss = (SCNHANDLE *)param;
assert(*ss); // Must have some code to run
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
_ctx->pic = InitInterpretContext(GS_SCENE, READ_LE_UINT32(ss), NOEVENT, NOPOLY, 0, NULL); // get the stuff copied to process when it was created
_ctx->pInit = (const TP_INIT *)param;
assert(_ctx->pInit);
assert(_ctx->pInit->hTinselCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_SCENE,
READ_LE_UINT32(&_ctx->pInit->hTinselCode),
TinselV2 ? _ctx->pInit->event : NOEVENT,
NOPOLY, // No polygon
0, // No actor
NULL, // No object
0);
CORO_INVOKE_1(Interpret, _ctx->pic); CORO_INVOKE_1(Interpret, _ctx->pic);
if (_ctx->pInit->event == CLOSEDOWN || _ctx->pInit->event == LEAVE_T2)
bWatchingOut = false;
CORO_END_CODE; CORO_END_CODE;
} }
/**
* Start up a SceneTinselProcess() running the scene
* script for various events.
*/
void SendSceneTinselProcess(TINSEL_EVENT event) {
SCENE_STRUC *ss;
if (event == CLOSEDOWN || event == LEAVE_T2)
bWatchingOut = true;
if (SceneHandle != (SCNHANDLE)NULL) {
ss = (SCENE_STRUC *) FindChunk(SceneHandle, CHUNK_SCENE);
if (ss->hSceneScript) {
TP_INIT init;
init.event = event;
init.hTinselCode = ss->hSceneScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
} else if (event == CLOSEDOWN)
bWatchingOut = false;
}
else if (event == CLOSEDOWN)
bWatchingOut = false;
}
/** /**
* Get the SCENE_STRUC * Get the SCENE_STRUC
* Initialise polygons for the scene * Initialise polygons for the scene
@ -128,47 +211,103 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
* Run the appropriate entrance code (if any) * Run the appropriate entrance code (if any)
* Get the default refer type * Get the default refer type
*/ */
static void LoadScene(SCNHANDLE scene, int entry) { static void LoadScene(SCNHANDLE scene, int entry) {
uint i;
TP_INIT init;
const SCENE_STRUC *ss; const SCENE_STRUC *ss;
const ENTRANCE_STRUC *es; const ENTRANCE_STRUC *es;
uint i;
// Scene structure // Scene handle
SceneHandle = scene; // Save scene handle in case of Save_Scene() SceneHandle = scene; // Save scene handle in case of Save_Scene()
LockMem(SceneHandle); // Make sure scene is loaded LockMem(SceneHandle); // Make sure scene is loaded
LockScene(SceneHandle); // Prevent current scene from being discarded LockScene(SceneHandle); // Prevent current scene from being discarded
ss = (const SCENE_STRUC *)FindChunk(scene, CHUNK_SCENE); if (TinselV2) {
// CdPlay() stuff
byte *cptr = FindChunk(scene, CHUNK_CDPLAY_FILENUM);
assert(cptr);
i = READ_LE_UINT32(cptr);
assert(i < 512);
cptr = FindChunk(scene, CHUNK_CDPLAY_FILENAME);
assert(cptr);
SetCdPlaySceneDetails(i, (const char *)cptr);
}
// Find scene structure
ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
assert(ss != NULL); assert(ss != NULL);
if (TinselV2) {
// Handle to scene description
newestScene = FROM_LE_32(ss->hSceneDesc);
// Music stuff
char *cptr = (char *)FindChunk(scene, CHUNK_MUSIC_FILENAME);
assert(cptr);
_vm->_pcmMusic->setMusicSceneDetails(FROM_LE_32(ss->hMusicScript),
FROM_LE_32(ss->hMusicSegment), cptr);
}
if (entry == NO_ENTRY_NUM) {
// Restoring scene
// Initialise all the polygons for this scene // Initialise all the polygons for this scene
InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), (entry == NO_ENTRY_NUM)); InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), true);
// Initialise the actors for this scene // Initialise the actors for this scene
StartActors(FROM_LE_32(ss->hActor), FROM_LE_32(ss->numActor), (entry != NO_ENTRY_NUM)); StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), false);
if (entry != NO_ENTRY_NUM) { if (TinselV2)
// Returning from cutscene
SendSceneTinselProcess(RESTORE);
} else {
// Genuine new scene
// Initialise all the polygons for this scene
InitPolygons(FROM_LE_32(ss->hPoly), FROM_LE_32(ss->numPoly), false);
// Initialise the actors for this scene
StartTaggedActors(FROM_LE_32(ss->hTaggedActor), FROM_LE_32(ss->numTaggedActor), true);
// Run the appropriate entrance code (if any) // Run the appropriate entrance code (if any)
es = (const ENTRANCE_STRUC *)LockMem(FROM_LE_32(ss->hEntrance)); es = (const ENTRANCE_STRUC *)LockMem(FROM_LE_32(ss->hEntrance));
for (i = 0; i < FROM_LE_32(ss->numEntrance); i++, es++) { for (i = 0; i < FROM_LE_32(ss->numEntrance); i++) {
if (FROM_LE_32(es->eNumber) == (uint)entry) { if (FROM_LE_32(es->eNumber) == (uint)entry) {
if (es->hScript) if (es->hScript) {
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &es->hScript, sizeof(es->hScript)); init.event = STARTUP;
init.hTinselCode = es->hScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
break; break;
} }
// Move to next entrance
if (TinselV2)
++es;
else
es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
} }
if (i == FROM_LE_32(ss->numEntrance)) if (i == FROM_LE_32(ss->numEntrance))
error("Non-existant scene entry number"); error("Non-existant scene entry number");
if (ss->hSceneScript) if (ss->hSceneScript) {
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &ss->hSceneScript, sizeof(ss->hSceneScript)); init.event = STARTUP;
init.hTinselCode = ss->hSceneScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
} }
// Default refer type // Default refer type
SetDefaultRefer(FROM_LE_32(ss->defRefer)); SetDefaultRefer(FROM_LE_32(ss->defRefer));
// Scene's processes
SceneProcesses(FROM_LE_32(ss->numProcess), FROM_LE_32(ss->hProcess));
} }
@ -184,15 +323,27 @@ void EndScene(void) {
KillInventory(); // Close down any open inventory KillInventory(); // Close down any open inventory
DropPolygons(); // No polygons DropPolygons(); // No polygons
DropNoScrolls(); // No no-scrolls DropScroll(); // No no-scrolls
DropBackground(); // No background DropBackground(); // No background
DropMActors(); // No moving actors DropMovers(); // No moving actors
DropCursor(); // No cursor DropCursor(); // No cursor
DropActors(); // No actor reels running DropActors(); // No actor reels running
FreeAllTokens(); // No-one has tokens FreeAllTokens(); // No-one has tokens
FreeMostInterpretContexts(); // Only master script still interpreting FreeMostInterpretContexts(); // Only master script still interpreting
if (TinselV2) {
SetSysVar(ISV_DIVERT_ACTOR, 0);
SetSysVar(ISV_GHOST_ACTOR, 0);
SetSysVar(SV_MinimumXoffset, 0);
SetSysVar(SV_MaximumXoffset, 0);
SetSysVar(SV_MinimumYoffset, 0);
SetSysVar(SV_MaximumYoffset, 0);
ResetFontHandles();
NoSoundReels();
}
_vm->_sound->stopAllSamples(); // Kill off any still-running sample _vm->_sound->stopAllSamples(); // Kill off any still-running sample
_vm->_mixer->stopAll();
// init the palette manager // init the palette manager
ResetPalAllocator(); ResetPalAllocator();
@ -251,10 +402,11 @@ void PrimeBackground(void) {
*/ */
void PrimeScene(void) { void PrimeScene(void) {
SetNoBlocking(false);
bNoBlocking = false; SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
RestartCursor(); // Restart the cursor RestartCursor(); // Restart the cursor
if (!TinselV2)
EnableTags(); // Next scene with tags enabled EnableTags(); // Next scene with tags enabled
g_scheduler->createProcess(PID_SCROLL, ScrollProcess, NULL, 0); g_scheduler->createProcess(PID_SCROLL, ScrollProcess, NULL, 0);
@ -276,9 +428,15 @@ void PrimeScene(void) {
* Wrap up the last scene and start up the next scene. * Wrap up the last scene and start up the next scene.
*/ */
void NewScene(SCNHANDLE scene, int entry) { void StartNewScene(SCNHANDLE scene, int entry) {
EndScene(); // Wrap up the last scene. EndScene(); // Wrap up the last scene.
if (TinselV2) {
TouchMoverReels();
LockMem(scene); // Do CD change before PrimeScene
}
PrimeScene(); // Start up the standard stuff for the next scene. PrimeScene(); // Start up the standard stuff for the next scene.
LoadScene(scene, entry); LoadScene(scene, entry);
@ -303,4 +461,29 @@ SCNHANDLE GetSceneHandle(void) {
return SceneHandle; return SceneHandle;
} }
/**
* DoHailScene
*/
void DoHailScene(SCNHANDLE scene) {
// Find scene structure
const SCENE_STRUC *ss = GetSceneStruc(FindChunk(scene, CHUNK_SCENE));
if (ss != NULL && ss->hSceneScript) {
TP_INIT init;
init.event = NOEVENT;
init.hTinselCode = ss->hSceneScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
}
/**
* WrapScene
*/
void WrapScene(void) {
SendSceneTinselProcess(CLOSEDOWN);
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -28,6 +28,7 @@
#define TINSEL_SCENE_H #define TINSEL_SCENE_H
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/events.h"
namespace Tinsel { namespace Tinsel {
@ -39,13 +40,19 @@ enum {
MAX_ACTOR = 32 //!< maximum number of actors in a scene MAX_ACTOR = 32 //!< maximum number of actors in a scene
}; };
// ENTRANCE_STRUC bitflags
enum ENTRANCE_FLAGS {
fCall = 0x00000001L,
fHook = 0x00000002L
};
/** reference direction */ /** reference direction */
enum REFTYPE { enum REFTYPE {
REF_DEFAULT, REF_UP, REF_DOWN, REF_LEFT, REF_RIGHT, REF_POINT REF_DEFAULT, REF_UP, REF_DOWN, REF_LEFT, REF_RIGHT, REF_POINT
}; };
enum TFTYPE { enum TFTYPE {
TF_NONE, TF_UP, TF_DOWN, TF_LEFT, TF_RIGHT, TF_BOGUS TF_NONE, TF_UP, TF_DOWN, TF_LEFT, TF_RIGHT, TF_FILM
}; };
/** different actor masks */ /** different actor masks */
@ -68,6 +75,23 @@ enum REEL {
REEL_DEFAULT, REEL_ALL, REEL_HORIZ, REEL_VERT REEL_DEFAULT, REEL_ALL, REEL_HORIZ, REEL_VERT
}; };
typedef enum { TRANS_DEF, TRANS_CUT, TRANS_FADE } TRANSITS;
// amount to shift scene handles by
#define SCNHANDLE_SHIFT (TinselV2 ? 25 : 23)
#define OFFSETMASK (TinselV2 ? 0x01ffffffL : 0x007fffffL)
#define HANDLEMASK (TinselV2 ? 0xFE000000L : 0xFF800000L)
void DoHailScene(SCNHANDLE scene);
void WrapScene(void);
void StartNewScene(SCNHANDLE scene, int entry);
void EndScene(void);
void SendSceneTinselProcess(TINSEL_EVENT event);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_SCENE_H #endif // TINSEL_SCENE_H

View file

@ -24,6 +24,10 @@
* Process scheduler. * Process scheduler.
*/ */
#include "tinsel/handle.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "common/util.h" #include "common/util.h"
@ -32,18 +36,26 @@ namespace Tinsel {
Scheduler *g_scheduler = 0; Scheduler *g_scheduler = 0;
/** process structure */ #include "common/pack-start.h" // START STRUCT PACKING
struct PROCESS {
PROCESS *pNext; //!< pointer to next process in active or free list
CoroContext state; //!< the state of the coroutine struct PROCESS_STRUC {
CORO_ADDR coroAddr; //!< the entry point of the coroutine uint32 processId; // ID of process
SCNHANDLE hProcessCode; // handle to actor script
} PACKED_STRUCT;
int sleepTime; //!< number of scheduler cycles to sleep #include "common/pack-end.h" // END STRUCT PACKING
int pid; //!< process ID
char param[PARAM_SIZE]; //!< process specific info
};
CoroContext nullContext = NULL;
//----------------- LOCAL GLOBAL DATA --------------------
static uint32 numSceneProcess;
static SCNHANDLE hSceneProcess;
static uint32 numGlobalProcess;
static PROCESS_STRUC *pGlobalProcess;
//--------------------- FUNCTIONS ------------------------
Scheduler::Scheduler() { Scheduler::Scheduler() {
processList = 0; processList = 0;
@ -59,6 +71,7 @@ Scheduler::Scheduler() {
pRCfunction = 0; pRCfunction = 0;
active = new PROCESS; active = new PROCESS;
active->pPrevious = NULL;
g_scheduler = this; // FIXME HACK g_scheduler = this; // FIXME HACK
} }
@ -83,7 +96,7 @@ void Scheduler::reset() {
if (processList == NULL) { if (processList == NULL) {
// first time - allocate memory for process list // first time - allocate memory for process list
processList = (PROCESS *)calloc(NUM_PROCESS, sizeof(PROCESS)); processList = (PROCESS *)calloc(MAX_PROCESSES, sizeof(PROCESS));
// make sure memory allocated // make sure memory allocated
if (processList == NULL) { if (processList == NULL) {
@ -91,7 +104,7 @@ void Scheduler::reset() {
} }
// fill with garbage // fill with garbage
memset(processList, 'S', NUM_PROCESS * sizeof(PROCESS)); memset(processList, 'S', MAX_PROCESSES * sizeof(PROCESS));
} }
// no active processes // no active processes
@ -101,12 +114,10 @@ void Scheduler::reset() {
pFreeProcesses = processList; pFreeProcesses = processList;
// link all other processes after first // link all other processes after first
for (int i = 1; i < NUM_PROCESS; i++) { for (int i = 1; i <= NUM_PROCESS; i++) {
processList[i - 1].pNext = processList + i; processList[i - 1].pNext = (i == NUM_PROCESS) ? NULL : processList + i;
processList[i - 1].pPrevious = (i == 1) ? active : processList + (i - 2);
} }
// null the last process
processList[NUM_PROCESS - 1].pNext = NULL;
} }
@ -119,33 +130,172 @@ void Scheduler::printStats(void) {
} }
#endif #endif
#ifdef DEBUG
/**
* Checks both the active and free process list to insure all the links are valid,
* and that no processes have been lost
*/
void Scheduler::CheckStack() {
Common::List<PROCESS *> pList;
// Check both the active and free process lists
for (int i = 0; i < 2; ++i) {
PROCESS *p = (i == 0) ? active : pFreeProcesses;
if (p != NULL) {
// Make sure the linkages are correct
while (p->pNext != NULL) {
assert(p->pNext->pPrevious == p);
pList.push_back(p);
p = p->pNext;
}
pList.push_back(p);
}
}
// Make sure all processes are accounted for
for (int idx = 0; idx < NUM_PROCESS; idx++) {
bool found = false;
for (Common::List<PROCESS *>::iterator i = pList.begin(); i != pList.end(); ++i) {
PROCESS *pTemp = *i;
if (*i == &processList[idx]) {
found = true;
break;
}
}
assert(found);
}
}
#endif
/** /**
* Give all active processes a chance to run * Give all active processes a chance to run
*/ */
void Scheduler::schedule(void) { void Scheduler::schedule(void) {
// start dispatching active process list // start dispatching active process list
PROCESS *pPrevProc = active; PROCESS *pNext;
PROCESS *pProc = active->pNext; PROCESS *pProc = active->pNext;
while (pProc != NULL) { while (pProc != NULL) {
pNext = pProc->pNext;
if (--pProc->sleepTime <= 0) { if (--pProc->sleepTime <= 0) {
// process is ready for dispatch, activate it // process is ready for dispatch, activate it
pCurrent = pProc; pCurrent = pProc;
pProc->coroAddr(pProc->state, pProc->param); pProc->coroAddr(pProc->state, pProc->param);
pCurrent = NULL;
if (!pProc->state || pProc->state->_sleep <= 0) { if (!pProc->state || pProc->state->_sleep <= 0) {
// Coroutine finished // Coroutine finished
pCurrent = pCurrent->pPrevious;
killProcess(pProc); killProcess(pProc);
pProc = pPrevProc;
} else { } else {
pProc->sleepTime = pProc->state->_sleep; pProc->sleepTime = pProc->state->_sleep;
} }
// pCurrent may have been changed
pNext = pCurrent->pNext;
pCurrent = NULL;
} }
pPrevProc = pProc;
pProc = pProc->pNext; pProc = pNext;
} }
} }
/**
* Reschedules all the processes to run again this query
*/
void Scheduler::rescheduleAll() {
assert(pCurrent);
// Unlink current process
pCurrent->pPrevious->pNext = pCurrent->pNext;
if (pCurrent->pNext)
pCurrent->pNext->pPrevious = pCurrent->pPrevious;
// Add process to the start of the active list
pCurrent->pNext = active->pNext;
active->pNext->pPrevious = pCurrent;
active->pNext = pCurrent;
pCurrent->pPrevious = active;
}
/**
* If the specified process has already run on this tick, make it run
* again on the current tick.
*/
void Scheduler::reschedule(PPROCESS pReSchedProc) {
// If not currently processing the schedule list, then no action is needed
if (!pCurrent)
return;
if (!pReSchedProc)
pReSchedProc = pCurrent;
PPROCESS pEnd;
// Find the last process in the list.
// But if the target process is down the list from here, do nothing
for (pEnd = pCurrent; pEnd->pNext != NULL; pEnd = pEnd->pNext) {
if (pEnd->pNext == pReSchedProc)
return;
}
assert(pEnd->pNext == NULL);
// Could be in the middle of a KillProc()!
// Dying process was last and this process was penultimate
if (pReSchedProc->pNext == NULL)
return;
// If we're moving the current process, move it back by one, so that the next
// schedule() iteration moves to the now next one
if (pCurrent == pReSchedProc)
pCurrent = pCurrent->pPrevious;
// Unlink the process, and add it at the end
pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
pEnd->pNext = pReSchedProc;
pReSchedProc->pPrevious = pEnd;
pReSchedProc->pNext = NULL;
}
/**
* Moves the specified process to the end of the dispatch queue
* allowing it to run again within the current game cycle.
* @param pGiveProc Which process
*/
void Scheduler::giveWay(PPROCESS pReSchedProc) {
// If not currently processing the schedule list, then no action is needed
if (!pCurrent)
return;
if (!pReSchedProc)
pReSchedProc = pCurrent;
// If the process is already at the end of the queue, nothing has to be done
if (!pReSchedProc->pNext)
return;
PPROCESS pEnd;
// Find the last process in the list.
for (pEnd = pCurrent; pEnd->pNext != NULL; pEnd = pEnd->pNext) ;
assert(pEnd->pNext == NULL);
// If we're moving the current process, move it back by one, so that the next
// schedule() iteration moves to the now next one
if (pCurrent == pReSchedProc)
pCurrent = pCurrent->pPrevious;
// Unlink the process, and add it at the end
pReSchedProc->pPrevious->pNext = pReSchedProc->pNext;
pReSchedProc->pNext->pPrevious = pReSchedProc->pPrevious;
pEnd->pNext = pReSchedProc;
pReSchedProc->pPrevious = pEnd;
pReSchedProc->pNext = NULL;
}
/** /**
* Creates a new process. * Creates a new process.
@ -172,16 +322,27 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
// get link to next free process // get link to next free process
pFreeProcesses = pProc->pNext; pFreeProcesses = pProc->pNext;
if (pFreeProcesses)
pFreeProcesses->pPrevious = NULL;
if (pCurrent != NULL) { if (pCurrent != NULL) {
// place new process before the next active process // place new process before the next active process
pProc->pNext = pCurrent->pNext; pProc->pNext = pCurrent->pNext;
if (pProc->pNext)
pProc->pNext->pPrevious = pProc;
// make this new process the next active process // make this new process the next active process
pCurrent->pNext = pProc; pCurrent->pNext = pProc;
pProc->pPrevious = pCurrent;
} else { // no active processes, place process at head of list } else { // no active processes, place process at head of list
pProc->pNext = active->pNext; pProc->pNext = active->pNext;
pProc->pPrevious = active;
if (pProc->pNext)
pProc->pNext->pPrevious = pProc;
active->pNext = pProc; active->pNext = pProc;
} }
// set coroutine entry point // set coroutine entry point
@ -204,7 +365,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
memcpy(pProc->param, pParam, sizeParam); memcpy(pProc->param, pParam, sizeParam);
} }
// return created process // return created process
return pProc; return pProc;
} }
@ -215,8 +375,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
* @param pKillProc which process to kill * @param pKillProc which process to kill
*/ */
void Scheduler::killProcess(PROCESS *pKillProc) { void Scheduler::killProcess(PROCESS *pKillProc) {
PROCESS *pProc, *pPrev; // process list pointers
// make sure a valid process pointer // make sure a valid process pointer
assert(pKillProc >= processList && pKillProc <= processList + NUM_PROCESS - 1); assert(pKillProc >= processList && pKillProc <= processList + NUM_PROCESS - 1);
@ -229,32 +387,25 @@ void Scheduler::killProcess(PROCESS *pKillProc) {
assert(numProcs >= 0); assert(numProcs >= 0);
#endif #endif
// search the active list for the process
for (pProc = active->pNext, pPrev = active; pProc != NULL; pPrev = pProc, pProc = pProc->pNext) {
if (pProc == pKillProc) {
// found process in active list
// Free process' resources // Free process' resources
if (pRCfunction != NULL) if (pRCfunction != NULL)
(pRCfunction)(pProc); (pRCfunction)(pKillProc);
delete pProc->state; delete pKillProc->state;
// make prev point to next to unlink pProc // Take the process out of the active chain list
pPrev->pNext = pProc->pNext; pKillProc->pPrevious->pNext = pKillProc->pNext;
if (pKillProc->pNext)
pKillProc->pNext->pPrevious = pKillProc->pPrevious;
// link first free process after pProc // link first free process after pProc
pProc->pNext = pFreeProcesses; pKillProc->pNext = pFreeProcesses;
if (pFreeProcesses)
pKillProc->pNext->pPrevious = pKillProc;
pKillProc->pPrevious = NULL;
// make pProc the first free process // make pKillProc the first free process
pFreeProcesses = pProc; pFreeProcesses = pKillProc;
return;
}
}
// process not found in active list if we get to here
error("killProcess(): tried to kill a process not in the list of active processes");
} }
@ -302,11 +453,21 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
// kill this process // kill this process
numKilled++; numKilled++;
// Free the process' resources
if (pRCfunction != NULL)
(pRCfunction)(pProc);
delete pProc->state;
// make prev point to next to unlink pProc // make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext; pPrev->pNext = pProc->pNext;
if (pProc->pNext)
pPrev->pNext->pPrevious = pPrev;
// link first free process after pProc // link first free process after pProc
pProc->pNext = pFreeProcesses; pProc->pNext = pFreeProcesses;
pProc->pPrevious = NULL;
pFreeProcesses->pPrevious = pProc;
// make pProc the first free process // make pProc the first free process
pFreeProcesses = pProc; pFreeProcesses = pProc;
@ -327,8 +488,6 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
return numKilled; return numKilled;
} }
/** /**
* Set pointer to a function to be called by killProcess(). * Set pointer to a function to be called by killProcess().
* *
@ -342,4 +501,262 @@ void Scheduler::setResourceCallback(VFPTRPP pFunc) {
pRCfunction = pFunc; pRCfunction = pFunc;
} }
/**************************************************************************\
|*********** Stuff to do with scene and global processes ************|
\**************************************************************************/
/**
* The code for for restored scene processes.
*/
static void RestoredProcessProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
PROCESS *pProc; // this process pointer
// get the stuff copied to process when it was created
pProc = g_scheduler->getCurrentProcess();
_ctx->pic = *((INT_CONTEXT **) pProc->param);
_ctx->pic = RestoreInterpretContext(_ctx->pic);
AttachInterpret(_ctx->pic, pProc);
CORO_INVOKE_1(Interpret, _ctx->pic);
CORO_END_CODE;
}
/**
* Process Tinsel Process
*/
static void ProcessTinselProcess(CORO_PARAM, const void *) {
PPROCESS pProc = g_scheduler->getCurrentProcess();
PINT_CONTEXT *pPic = (PINT_CONTEXT *) pProc->param;
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// get the stuff copied to process when it was created
CORO_INVOKE_1(Interpret, *pPic);
CORO_KILL_SELF();
CORO_END_CODE;
}
/**************************************************************************\
|***************** Stuff to do with scene processes *****************|
\**************************************************************************/
/**
* Called to restore a scene process.
*/
void RestoreSceneProcess(INT_CONTEXT *pic) {
uint32 i;
PROCESS_STRUC *pStruc;
pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess);
for (i = 0; i < numSceneProcess; i++) {
if (pStruc[i].hProcessCode == pic->hCode) {
g_scheduler->createProcess(PID_PROCESS + i, RestoredProcessProcess,
&pic, sizeof(pic));
break;
}
}
assert(i < numSceneProcess);
}
/**
* Run a scene process with the given event.
*/
void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape,
bool *result) {
uint32 i; // Loop counter
if (result) *result = false;
CORO_BEGIN_CONTEXT;
PROCESS_STRUC *pStruc;
PPROCESS pProc;
PINT_CONTEXT pic;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
_ctx->pStruc = (PROCESS_STRUC *)LockMem(hSceneProcess);
for (i = 0; i < numSceneProcess; i++) {
if (_ctx->pStruc[i].processId == procID) {
assert(_ctx->pStruc[i].hProcessCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_PROCESS,
_ctx->pStruc[i].hProcessCode,
event,
NOPOLY, // No polygon
0, // No actor
NULL, // No object
myEscape);
if (_ctx->pic == NULL)
return;
_ctx->pProc = g_scheduler->createProcess(PID_PROCESS + i, ProcessTinselProcess,
&_ctx->pic, sizeof(_ctx->pic));
AttachInterpret(_ctx->pic, _ctx->pProc);
break;
}
}
if (i == numSceneProcess)
return;
if (bWait) {
CORO_INVOKE_2(WaitInterpret, _ctx->pProc, result);
}
CORO_END_CODE;
}
/**
* Kill all instances of a scene process.
*/
void KillSceneProcess(uint32 procID) {
uint32 i; // Loop counter
PROCESS_STRUC *pStruc;
pStruc = (PROCESS_STRUC *) LockMem(hSceneProcess);
for (i = 0; i < numSceneProcess; i++) {
if (pStruc[i].processId == procID) {
g_scheduler->killMatchingProcess(PID_PROCESS + i, -1);
break;
}
}
}
/**
* Register the scene processes in a scene.
*/
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess) {
numSceneProcess = numProcess;
hSceneProcess = hProcess;
}
/**************************************************************************\
|***************** Stuff to do with global processes ****************|
\**************************************************************************/
/**
* Called to restore a global process.
*/
void RestoreGlobalProcess(INT_CONTEXT *pic) {
uint32 i; // Loop counter
for (i = 0; i < numGlobalProcess; i++) {
if (pGlobalProcess[i].hProcessCode == pic->hCode) {
g_scheduler->createProcess(PID_GPROCESS + i, RestoredProcessProcess,
&pic, sizeof(pic));
break;
}
}
assert(i < numGlobalProcess);
}
/**
* Kill them all (restore game).
*/
void KillGlobalProcesses(void) {
for (uint32 i = 0; i < numGlobalProcess; ++i) {
g_scheduler->killMatchingProcess(PID_GPROCESS + i, -1);
}
}
/**
* Run a global process with the given event.
*/
bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape) {
CORO_BEGIN_CONTEXT;
PINT_CONTEXT pic;
PPROCESS pProc;
CORO_END_CONTEXT(_ctx);
bool result = false;
CORO_BEGIN_CODE(_ctx);
uint32 i; // Loop counter
_ctx->pProc = NULL;
for (i = 0; i < numGlobalProcess; ++i) {
if (pGlobalProcess[i].processId == procID) {
assert(pGlobalProcess[i].hProcessCode); // Must have some code to run
_ctx->pic = InitInterpretContext(GS_GPROCESS,
pGlobalProcess[i].hProcessCode,
event,
NOPOLY, // No polygon
0, // No actor
NULL, // No object
myEscape);
if (_ctx->pic != NULL) {
_ctx->pProc = g_scheduler->createProcess(PID_GPROCESS + i, ProcessTinselProcess,
&_ctx->pic, sizeof(_ctx->pic));
AttachInterpret(_ctx->pic, _ctx->pProc);
}
break;
}
}
if ((i == numGlobalProcess) || (_ctx->pic == NULL))
result = false;
else if (bWait)
CORO_INVOKE_ARGS_V(WaitInterpret, false, (CORO_SUBCTX, _ctx->pProc, &result));
CORO_END_CODE;
return result;
}
/**
* Kill all instances of a global process.
*/
void xKillGlobalProcess(uint32 procID) {
uint32 i; // Loop counter
for (i = 0; i < numGlobalProcess; ++i) {
if (pGlobalProcess[i].processId == procID) {
g_scheduler->killMatchingProcess(PID_GPROCESS + i, -1);
break;
}
}
}
/**
* Register the global processes list
*/
void GlobalProcesses(uint32 numProcess, byte *pProcess) {
pGlobalProcess = new PROCESS_STRUC[numProcess];
numGlobalProcess = numProcess;
byte *p = pProcess;
for (uint i = 0; i < numProcess; ++i, p += 8) {
pGlobalProcess[i].processId = READ_LE_UINT32(p);
pGlobalProcess[i].hProcessCode = READ_LE_UINT32(p + 4);
}
}
/**
* Frees the global processes list
*/
void FreeGlobalProcesses() {
delete[] pGlobalProcess;
numGlobalProcess = 0;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -29,6 +29,8 @@
#include "tinsel/dw.h" // new data types #include "tinsel/dw.h" // new data types
#include "tinsel/coroutine.h" #include "tinsel/coroutine.h"
#include "tinsel/events.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
@ -36,12 +38,26 @@ namespace Tinsel {
#define PARAM_SIZE 32 #define PARAM_SIZE 32
// the maximum number of processes // the maximum number of processes
#define NUM_PROCESS 64 #define NUM_PROCESS (TinselV2 ? 70 : 64)
#define MAX_PROCESSES 70
typedef void (*CORO_ADDR)(CoroContext &, const void *); typedef void (*CORO_ADDR)(CoroContext &, const void *);
/** process structure */
struct PROCESS {
PROCESS *pNext; //!< pointer to next process in active or free list
PROCESS *pPrevious; //!< pointer to previous process in active or free list
struct PROCESS; CoroContext state; //!< the state of the coroutine
CORO_ADDR coroAddr; //!< the entry point of the coroutine
int sleepTime; //!< number of scheduler cycles to sleep
int pid; //!< process ID
char param[PARAM_SIZE]; //!< process specific info
};
typedef PROCESS *PPROCESS;
struct INT_CONTEXT;
/** /**
* Create and manage "processes" (really coroutines). * Create and manage "processes" (really coroutines).
@ -69,6 +85,8 @@ private:
// diagnostic process counters // diagnostic process counters
int numProcs; int numProcs;
int maxProcs; int maxProcs;
void CheckStack();
#endif #endif
/** /**
@ -90,13 +108,16 @@ public:
#endif #endif
void schedule(); void schedule();
void rescheduleAll();
void reschedule(PPROCESS pReSchedProc = NULL);
void giveWay(PPROCESS pReSchedProc = NULL);
PROCESS *createProcess(int pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam); PROCESS *createProcess(int pid, CORO_ADDR coroAddr, const void *pParam, int sizeParam);
void killProcess(PROCESS *pKillProc); void killProcess(PROCESS *pKillProc);
PROCESS *getCurrentProcess(); PROCESS *getCurrentProcess();
int getCurrentPID() const; int getCurrentPID() const;
int killMatchingProcess(int pidKill, int pidMask); int killMatchingProcess(int pidKill, int pidMask = -1);
void setResourceCallback(VFPTRPP pFunc); void setResourceCallback(VFPTRPP pFunc);
@ -105,6 +126,23 @@ public:
extern Scheduler *g_scheduler; // FIXME: Temporary global var, to be used until everything has been OOifyied extern Scheduler *g_scheduler; // FIXME: Temporary global var, to be used until everything has been OOifyied
//----------------- FUNCTION PROTOTYPES --------------------
void SceneProcesses(uint32 numProcess, SCNHANDLE hProcess);
void CallSceneProcess(uint32 procID);
void KillSceneProcess(uint32 procID);
void SceneProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait,
int myEscape, bool *result = NULL);
void RestoreSceneProcess(INT_CONTEXT *pic);
void GlobalProcesses(uint32 numProcess, byte *pProcess);
void xCallGlobalProcess(uint32 procID);
void xKillGlobalProcess(uint32 procID);
bool GlobalProcessEvent(CORO_PARAM, uint32 procID, TINSEL_EVENT event, bool bWait, int myEscape);
void RestoreGlobalProcess(INT_CONTEXT *pic);
void KillGlobalProcesses(void);
void FreeGlobalProcesses();
} // end of namespace Tinsel } // end of namespace Tinsel
#endif // TINSEL_SCHED_H #endif // TINSEL_SCHED_H

View file

@ -47,10 +47,15 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
uint32 *lptr = (uint32 *)bptr; uint32 *lptr = (uint32 *)bptr;
uint32 add; uint32 add;
// V1 chunk types can be found by substracting 2 from the // Initial adjustmnet for Tinsel 1 chunk types
if ((TinselVersion != TINSEL_V2) && (chunk >= CHUNK_SCENE) &&
(chunk != CHUNK_MBSTRING))
--chunk;
// V0 chunk types can be found by substracting 2 from the
// chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are // chunk type. Note that CHUNK_STRING and CHUNK_BITMAP are
// the same in V1 and V2 // the same in V0 and V1
if (_vm->getVersion() == TINSEL_V0 && if (TinselVersion == TINSEL_V0 &&
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP) chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
chunk -= 0x2L; chunk -= 0x2L;
@ -70,10 +75,10 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
/** /**
* Get the actor id from a film (column 0) * Get the actor id from a film (column 0)
*/ */
int extractActor(SCNHANDLE film) { int ExtractActor(SCNHANDLE hFilm) {
const FILM *pfilm = (const FILM *)LockMem(film); const FILM *pFilm = (const FILM *)LockMem(hFilm);
const FREEL *preel = &pfilm->reels[0]; const FREEL *pReel = &pFilm->reels[0];
const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(preel->mobj)); const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj));
return (int)FROM_LE_32(pmi->mulID); return (int)FROM_LE_32(pmi->mulID);
} }

View file

@ -30,6 +30,14 @@
namespace Tinsel { namespace Tinsel {
#define INDEX_FILENAME "index" // name of scene index file
#define INDEXFILE_LENGTH 12 // length of filenames in the MEMHANDLE structure
#define GLOBALS_FILENAME "gdata" // name of globals file
#define HOPPER_FILENAME "hopper"
#define CD_ID_FILENAME "volume"
#define BMOVIE_EXTENSION ".bmv"
// chunk identifier numbers // chunk identifier numbers
@ -48,20 +56,42 @@ namespace Tinsel {
#define CHUNK_ENTRANCE 0x3334000BL // not used! #define CHUNK_ENTRANCE 0x3334000BL // not used!
#define CHUNK_POLYGONS 0x3334000CL // not used! #define CHUNK_POLYGONS 0x3334000CL // not used!
#define CHUNK_ACTORS 0x3334000DL // not used! #define CHUNK_ACTORS 0x3334000DL // not used!
#define CHUNK_SCENE 0x3334000EL
#define CHUNK_TOTAL_ACTORS 0x3334000FL #define CHUNK_PROCESSES 0x3334000EL // Tinsel 2 only
#define CHUNK_TOTAL_GLOBALS 0x33340010L
#define CHUNK_TOTAL_OBJECTS 0x33340011L // Following chunk Ids should be decremented by 1 for Tinsel 1
#define CHUNK_OBJECTS 0x33340012L #define CHUNK_SCENE 0x3334000FL
#define CHUNK_MIDI 0x33340013L // not used! #define CHUNK_TOTAL_ACTORS 0x33340010L
#define CHUNK_SAMPLE 0x33340014L // not used! #define CHUNK_TOTAL_GLOBALS 0x33340011L
#define CHUNK_TOTAL_POLY 0x33340015L #define CHUNK_TOTAL_OBJECTS 0x33340012L
#define CHUNK_MBSTRING 0x33340022L // Multi-byte characters #define CHUNK_OBJECTS 0x33340013L
#define CHUNK_MIDI 0x33340014L // not used!
#define CHUNK_SAMPLE 0x33340015L // not used!
#define CHUNK_TOTAL_POLY 0x33340016L
// Following chunks are Tinsel 2 only
#define CHUNK_NUM_PROCESSES 0x33340017L // Master scene only
#define CHUNK_MASTER_SCRIPT 0x33340018L
#define CHUNK_CDPLAY_FILENUM 0x33340019L
#define CHUNK_CDPLAY_HANDLE 0x3334001AL
#define CHUNK_CDPLAY_FILENAME 0x3334001BL
#define CHUNK_MUSIC_FILENAME 0x3334001CL
#define CHUNK_MUSIC_SCRIPT 0x3334001DL
#define CHUNK_MUSIC_SEGMENT 0x3334001EL
#define CHUNK_SCENE_HOPPER 0x3334001FL // Hopper file only
#define CHUNK_SCENE_HOPPER2 0x33340030L // Hopper file only
#define CHUNK_TIME_STAMPS 0x33340020L
// This single chunk is common to all Tinsel versions
#define CHUNK_MBSTRING 0x33340022L
// This is a base, subsequent numbers may also get used
#define CHUNK_GRAB_NAME 0x33340100L
#define INDEX_FILENAME "index" // name of index file #define INDEX_FILENAME "index" // name of index file
byte *FindChunk(SCNHANDLE handle, uint32 chunk); byte *FindChunk(SCNHANDLE handle, uint32 chunk);
int extractActor(SCNHANDLE film); int ExtractActor(SCNHANDLE hFilm);
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -33,17 +33,11 @@
#include "tinsel/rince.h" #include "tinsel/rince.h"
#include "tinsel/scroll.h" #include "tinsel/scroll.h"
#include "tinsel/sched.h" #include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel { namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
// in BG.C
extern int BackgroundWidth(void);
extern int BackgroundHeight(void);
//----------------- LOCAL DEFINES -------------------- //----------------- LOCAL DEFINES --------------------
#define LEFT 'L' #define LEFT 'L'
@ -58,7 +52,7 @@ extern int BackgroundHeight(void);
static int LeftScroll = 0, DownScroll = 0; // Number of iterations outstanding static int LeftScroll = 0, DownScroll = 0; // Number of iterations outstanding
static int scrollActor = 0; static int scrollActor = 0;
static PMACTOR psActor = 0; static PMOVER pScrollMover = 0;
static int oldx = 0, oldy = 0; static int oldx = 0, oldy = 0;
/** Boundaries and numbers of boundaries */ /** Boundaries and numbers of boundaries */
@ -72,6 +66,14 @@ static SCROLLDATA sd = {
{0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0}
}, },
0, 0,
0,
// DW2 fields
0,
0,
0,
0,
0,
0,
0 0
}; };
@ -81,7 +83,8 @@ static bool ScrollCursor = 0; // If a TAG or EXIT polygon is clicked on,
// the cursor is kept over that polygon // the cursor is kept over that polygon
// whilst scrolling // whilst scrolling
static int scrollPixels = SCROLLPIXELS; static int scrollPixelsX = SCROLLPIXELS;
static int scrollPixelsY = SCROLLPIXELS;
/** /**
@ -123,13 +126,6 @@ void SetNoScroll(int x1, int y1, int x2, int y2) {
} }
} }
/**
* Does the obvious - called at the end of a scene.
*/
void DropNoScrolls(void) {
sd.NumNoH = sd.NumNoV = 0;
}
/** /**
* Called from scroll process when it thinks that a scroll is in order. * Called from scroll process when it thinks that a scroll is in order.
* Checks for no-scroll boundaries and sets off a scroll if allowed. * Checks for no-scroll boundaries and sets off a scroll if allowed.
@ -157,9 +153,14 @@ static void NeedScroll(int direction) {
} }
if (LeftScroll <= 0) { if (LeftScroll <= 0) {
scrollPixels = SCROLLPIXELS; if (TinselV2) {
scrollPixelsX = sd.xSpeed;
LeftScroll += sd.xDistance;
} else {
scrollPixelsX = SCROLLPIXELS;
LeftScroll = RLSCROLL; LeftScroll = RLSCROLL;
} }
}
break; break;
case RIGHT: /* Picture will go right, 'camera' left */ case RIGHT: /* Picture will go right, 'camera' left */
@ -175,9 +176,14 @@ static void NeedScroll(int direction) {
} }
if (LeftScroll >= 0) { if (LeftScroll >= 0) {
scrollPixels = SCROLLPIXELS; if (TinselV2) {
scrollPixelsX = sd.xSpeed;
LeftScroll -= sd.xDistance;
} else {
scrollPixelsX = SCROLLPIXELS;
LeftScroll = -RLSCROLL; LeftScroll = -RLSCROLL;
} }
}
break; break;
case UP: /* Picture will go upwards, 'camera' downwards */ case UP: /* Picture will go upwards, 'camera' downwards */
@ -194,9 +200,14 @@ static void NeedScroll(int direction) {
} }
if (DownScroll <= 0) { if (DownScroll <= 0) {
scrollPixels = SCROLLPIXELS; if (TinselV2) {
scrollPixelsY = sd.ySpeed;
DownScroll += sd.yDistance;
} else {
scrollPixelsY = SCROLLPIXELS;
DownScroll = UDSCROLL; DownScroll = UDSCROLL;
} }
}
break; break;
case DOWN: /* Picture will go downwards, 'camera' upwards */ case DOWN: /* Picture will go downwards, 'camera' upwards */
@ -212,9 +223,14 @@ static void NeedScroll(int direction) {
} }
if (DownScroll >= 0) { if (DownScroll >= 0) {
scrollPixels = SCROLLPIXELS; if (TinselV2) {
scrollPixelsY = sd.ySpeed;
DownScroll -= sd.yDistance;
} else {
scrollPixelsY = SCROLLPIXELS;
DownScroll = -UDSCROLL; DownScroll = -UDSCROLL;
} }
}
break; break;
} }
} }
@ -234,7 +250,7 @@ static void ScrollImage(void) {
* Keeping cursor on a tag? * Keeping cursor on a tag?
*/ */
if (ScrollCursor) { if (ScrollCursor) {
GetCursorXY(&curX, &curY, true); GetCursorXYNoWait(&curX, &curY, true);
if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) { if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) {
OldLoffset = Loffset; OldLoffset = Loffset;
OldToffset = Toffset; OldToffset = Toffset;
@ -246,49 +262,66 @@ static void ScrollImage(void) {
* Horizontal scrolling * Horizontal scrolling
*/ */
if (LeftScroll > 0) { if (LeftScroll > 0) {
LeftScroll -= scrollPixels; LeftScroll -= scrollPixelsX;
if (LeftScroll < 0) { if (LeftScroll < 0) {
Loffset += LeftScroll; Loffset += LeftScroll;
LeftScroll = 0; LeftScroll = 0;
} }
Loffset += scrollPixels; // Move right Loffset += scrollPixelsX; // Move right
if (Loffset > ImageW - SCREEN_WIDTH) if (Loffset > ImageW - SCREEN_WIDTH)
Loffset = ImageW - SCREEN_WIDTH;// Now at extreme right Loffset = ImageW - SCREEN_WIDTH;// Now at extreme right
/*** New feature to prop up rickety scroll boundaries ***/
if (TinselV2 && SysVar(SV_MaximumXoffset) && (Loffset > SysVar(SV_MaximumXoffset)))
Loffset = SysVar(SV_MaximumXoffset);
} else if (LeftScroll < 0) { } else if (LeftScroll < 0) {
LeftScroll += scrollPixels; LeftScroll += scrollPixelsX;
if (LeftScroll > 0) { if (LeftScroll > 0) {
Loffset += LeftScroll; Loffset += LeftScroll;
LeftScroll = 0; LeftScroll = 0;
} }
Loffset -= scrollPixels; // Move left Loffset -= scrollPixelsX; // Move left
if (Loffset < 0) if (Loffset < 0)
Loffset = 0; // Now at extreme left Loffset = 0; // Now at extreme left
/*** New feature to prop up rickety scroll boundaries ***/
if (TinselV2 && SysVar(SV_MinimumXoffset) && (Loffset < SysVar(SV_MinimumXoffset)))
Loffset = SysVar(SV_MinimumXoffset);
} }
/* /*
* Vertical scrolling * Vertical scrolling
*/ */
if (DownScroll > 0) { if (DownScroll > 0) {
DownScroll -= scrollPixels; DownScroll -= scrollPixelsY;
if (DownScroll < 0) { if (DownScroll < 0) {
Toffset += DownScroll; Toffset += DownScroll;
DownScroll = 0; DownScroll = 0;
} }
Toffset += scrollPixels; // Move down Toffset += scrollPixelsY; // Move down
if (Toffset > ImageH - SCREEN_HEIGHT) if (Toffset > ImageH - SCREEN_HEIGHT)
Toffset = ImageH - SCREEN_HEIGHT;// Now at extreme bottom Toffset = ImageH - SCREEN_HEIGHT;// Now at extreme bottom
/*** New feature to prop up rickety scroll boundaries ***/
if (TinselV2 && SysVar(SV_MaximumYoffset) && Toffset > SysVar(SV_MaximumYoffset))
Toffset = SysVar(SV_MaximumYoffset);
} else if (DownScroll < 0) { } else if (DownScroll < 0) {
DownScroll += scrollPixels; DownScroll += scrollPixelsY;
if (DownScroll > 0) { if (DownScroll > 0) {
Toffset += DownScroll; Toffset += DownScroll;
DownScroll = 0; DownScroll = 0;
} }
Toffset -= scrollPixels; // Move up Toffset -= scrollPixelsY; // Move up
if (Toffset < 0) if (Toffset < 0)
Toffset = 0; // Now at extreme top Toffset = 0; // Now at extreme top
/*** New feature to prop up rickety scroll boundaries ***/
if (TinselV2 && SysVar(SV_MinimumYoffset) && Toffset < SysVar(SV_MinimumYoffset))
Toffset = SysVar(SV_MinimumYoffset);
} }
/* /*
@ -312,8 +345,7 @@ static void MonitorScroll(void) {
/* /*
* Only do it if the actor is there and is visible * Only do it if the actor is there and is visible
*/ */
if (!psActor || getMActorHideState(psActor) if (!pScrollMover || MoverHidden(pScrollMover) || !MoverIs(pScrollMover))
|| getMActorState(psActor) == NO_MACTOR)
return; return;
GetActorPos(scrollActor, &newx, &newy); GetActorPos(scrollActor, &newx, &newy);
@ -326,7 +358,7 @@ static void MonitorScroll(void) {
/* /*
* Approaching right side or left side of the screen? * Approaching right side or left side of the screen?
*/ */
if (newx > Loffset+SCREEN_WIDTH-RLDISTANCE && Loffset < ImageW-SCREEN_WIDTH) { if (newx > Loffset+SCREEN_WIDTH - RLDISTANCE && Loffset < ImageW - SCREEN_WIDTH) {
if (newx > oldx) if (newx > oldx)
NeedScroll(LEFT); NeedScroll(LEFT);
} else if (newx < Loffset + RLDISTANCE && Loffset) { } else if (newx < Loffset + RLDISTANCE && Loffset) {
@ -337,10 +369,10 @@ static void MonitorScroll(void) {
/* /*
* Approaching bottom or top of the screen? * Approaching bottom or top of the screen?
*/ */
if (newy > Toffset+SCREEN_HEIGHT-UDDISTANCE && Toffset < ImageH-SCREEN_HEIGHT) { if (newy > Toffset+SCREEN_HEIGHT-DDISTANCE && Toffset < ImageH-SCREEN_HEIGHT) {
if (newy > oldy) if (newy > oldy)
NeedScroll(UP); NeedScroll(UP);
} else if (Toffset && newy < Toffset + UDDISTANCE + GetActorBottom(scrollActor) - GetActorTop(scrollActor)) { } else if (Toffset && newy < Toffset + UDISTANCE + GetActorBottom(scrollActor) - GetActorTop(scrollActor)) {
if (newy < oldy) if (newy < oldy)
NeedScroll(DOWN); NeedScroll(DOWN);
} }
@ -349,6 +381,30 @@ static void MonitorScroll(void) {
oldy = newy; oldy = newy;
} }
static void RestoreScrollDefaults(void) {
sd.xTrigger = SysVar(SV_SCROLL_XTRIGGER);
sd.xDistance = SysVar(SV_SCROLL_XDISTANCE);
sd.xSpeed = SysVar(SV_SCROLL_XSPEED);
sd.yTriggerTop = SysVar(SV_SCROLL_YTRIGGERTOP);
sd.yTriggerBottom= SysVar(SV_SCROLL_YTRIGGERBOT);
sd.yDistance = SysVar(SV_SCROLL_YDISTANCE);
sd.ySpeed = SysVar(SV_SCROLL_YSPEED);
}
/**
* Does the obvious - called at the end of a scene.
*/
void DropScroll(void) {
sd.NumNoH = sd.NumNoV = 0;
if (TinselV2) {
LeftScroll = DownScroll = 0; // No iterations outstanding
oldx = oldy = 0;
scrollPixelsX = sd.xSpeed;
scrollPixelsY = sd.ySpeed;
RestoreScrollDefaults();
}
}
/** /**
* Decide when to scroll and scroll when decided to. * Decide when to scroll and scroll when decided to.
*/ */
@ -359,21 +415,28 @@ void ScrollProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
ImageH = BackgroundHeight(); // Dimensions // In Tinsel v2, scenes may play movies, so the background may not always
ImageW = BackgroundWidth(); // of this scene. // already be initialised like it is in v1
while (!GetBgObject())
CORO_SLEEP(1);
ImageH = BgHeight(); // Dimensions
ImageW = BgWidth(); // of this scene.
// Give up if there'll be no purpose in this process // Give up if there'll be no purpose in this process
if (ImageW == SCREEN_WIDTH && ImageH == SCREEN_HEIGHT) if (ImageW == SCREEN_WIDTH && ImageH == SCREEN_HEIGHT)
CORO_KILL_SELF(); CORO_KILL_SELF();
if (!TinselV2) {
LeftScroll = DownScroll = 0; // No iterations outstanding LeftScroll = DownScroll = 0; // No iterations outstanding
oldx = oldy = 0; oldx = oldy = 0;
scrollPixels = SCROLLPIXELS; scrollPixelsX = scrollPixelsY = SCROLLPIXELS;
}
if (!scrollActor) if (!scrollActor)
scrollActor = LeadId(); scrollActor = GetLeadId();
psActor = GetMover(scrollActor); pScrollMover = GetMover(scrollActor);
while (1) { while (1) {
MonitorScroll(); // Set scroll requirement MonitorScroll(); // Set scroll requirement
@ -395,17 +458,26 @@ void ScrollFocus(int ano) {
oldx = oldy = 0; oldx = oldy = 0;
scrollActor = ano; scrollActor = ano;
psActor = ano ? GetMover(scrollActor) : NULL; pScrollMover = ano ? GetMover(scrollActor) : NULL;
} }
} }
/**
* Returns the actor which the camera is following
*/
int GetScrollFocus(void) {
return scrollActor;
}
/** /**
* Scroll to abslote position. * Scroll to abslote position.
*/ */
void ScrollTo(int x, int y, int iter) { void ScrollTo(int x, int y, int xIter, int yIter) {
int Loffset, Toffset; // for background offsets int Loffset, Toffset; // for background offsets
scrollPixels = iter != 0 ? iter : SCROLLPIXELS; scrollPixelsX = xIter != 0 ? xIter : (TinselV2 ? sd.xSpeed : SCROLLPIXELS);
scrollPixelsY = yIter != 0 ? yIter : (TinselV2 ? sd.ySpeed : SCROLLPIXELS);
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); // get background offsets PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset); // get background offsets
@ -429,4 +501,35 @@ void RestoreNoScrollData(SCROLLDATA *ssd) {
memcpy(&sd, ssd, sizeof(SCROLLDATA)); memcpy(&sd, ssd, sizeof(SCROLLDATA));
} }
/**
* SetScrollParameters
*/
void SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
int yTriggerBottom, int yDistance, int ySpeed) {
if (xTrigger == 0 && xDistance == 0 && xSpeed == 0
&& yTriggerTop == 0 && yTriggerBottom && yDistance == 0 && ySpeed == 0) {
// Restore defaults
RestoreScrollDefaults();
} else {
if (xTrigger)
sd.xTrigger = xTrigger;
if (xDistance)
sd.xDistance = xDistance;
if (xSpeed)
sd.xSpeed = xSpeed;
if (yTriggerTop)
sd.yTriggerTop = yTriggerTop;
if (yTriggerBottom)
sd.yTriggerBottom = yTriggerBottom;
if (yDistance)
sd.yDistance = yDistance;
if (ySpeed)
sd.ySpeed = ySpeed;
}
}
bool IsScrolling(void) {
return (LeftScroll || DownScroll);
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -30,8 +30,10 @@ namespace Tinsel {
#define SCROLLPIXELS 8 // Number of pixels to scroll per iteration #define SCROLLPIXELS 8 // Number of pixels to scroll per iteration
#define RLDISTANCE 50 // Distance from edge that triggers a scroll // Distance from edge that triggers a scroll
#define UDDISTANCE 20 #define RLDISTANCE (TinselV2 ? sd.xTrigger : 50)
#define UDISTANCE (TinselV2 ? sd.yTriggerTop : 20)
#define DDISTANCE (TinselV2 ? sd.yTriggerBottom : 20)
// Number of iterations to make // Number of iterations to make
#define RLSCROLL 160 // 20*8 = 160 = half a screen #define RLSCROLL 160 // 20*8 = 160 = half a screen
@ -52,26 +54,39 @@ struct SCROLLDATA{
NOSCROLLB NoVScroll[MAX_VNOSCROLL]; // Vertical no-scroll boundaries NOSCROLLB NoVScroll[MAX_VNOSCROLL]; // Vertical no-scroll boundaries
NOSCROLLB NoHScroll[MAX_HNOSCROLL]; // Horizontal no-scroll boundaries NOSCROLLB NoHScroll[MAX_HNOSCROLL]; // Horizontal no-scroll boundaries
unsigned NumNoV, NumNoH; // Counts of no-scroll boundaries unsigned NumNoV, NumNoH; // Counts of no-scroll boundaries
// DW2 fields
int xTrigger;
int xDistance;
int xSpeed;
int yTriggerTop;
int yTriggerBottom;
int yDistance;
int ySpeed;
}; };
void DontScrollCursor(void); void DontScrollCursor(void);
void DoScrollCursor(void); void DoScrollCursor(void);
void SetNoScroll(int x1, int y1, int x2, int y2); void SetNoScroll(int x1, int y1, int x2, int y2);
void DropNoScrolls(void); void DropScroll(void);
void ScrollProcess(CORO_PARAM, const void *); void ScrollProcess(CORO_PARAM, const void *);
void ScrollFocus(int actor); void ScrollFocus(int actor);
void ScrollTo(int x, int y, int iter); int GetScrollFocus(void);
void ScrollTo(int x, int y, int xIter, int yIter);
void KillScroll(void); void KillScroll(void);
void GetNoScrollData(SCROLLDATA *ssd); void GetNoScrollData(SCROLLDATA *ssd);
void RestoreNoScrollData(SCROLLDATA *ssd); void RestoreNoScrollData(SCROLLDATA *ssd);
void SetScrollParameters(int xTrigger, int xDistance, int xSpeed, int yTriggerTop,
int yTriggerBottom, int yDistance, int ySpeed);
bool IsScrolling(void);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif /* TINSEL_SCROLL_H */ #endif /* TINSEL_SCROLL_H */

View file

@ -31,6 +31,8 @@
#include "tinsel/music.h" #include "tinsel/music.h"
#include "tinsel/strres.h" #include "tinsel/strres.h"
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
#include "tinsel/sysvar.h"
#include "tinsel/background.h"
#include "common/endian.h" #include "common/endian.h"
#include "common/file.h" #include "common/file.h"
@ -38,14 +40,20 @@
#include "sound/mixer.h" #include "sound/mixer.h"
#include "sound/audiocd.h" #include "sound/audiocd.h"
#include "sound/adpcm.h"
namespace Tinsel { namespace Tinsel {
extern LANGUAGE sampleLanguage;
//--------------------------- General data ---------------------------------- //--------------------------- General data ----------------------------------
SoundManager::SoundManager(TinselEngine *vm) : SoundManager::SoundManager(TinselEngine *vm) :
//_vm(vm), // TODO: Enable this once global _vm var is gone //_vm(vm), // TODO: Enable this once global _vm var is gone
_sampleIndex(0), _sampleIndexLen(0) { _sampleIndex(0), _sampleIndexLen(0) {
for (int i = 0; i < kNumChannels; i++)
_channels[i].sampleNum = _channels[i].subSample = -1;
} }
SoundManager::~SoundManager() { SoundManager::~SoundManager() {
@ -67,24 +75,29 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
if (!_vm->_mixer->isReady()) if (!_vm->_mixer->isReady())
return false; return false;
Channel &curChan = _channels[kChannelTinsel1];
// stop any currently playing sample // stop any currently playing sample
_vm->_mixer->stopHandle(_handle); _vm->_mixer->stopHandle(curChan.handle);
// make sure id is in range // make sure id is in range
assert(id > 0 && id < _sampleIndexLen); assert(id > 0 && id < _sampleIndexLen);
curChan.sampleNum = id;
curChan.subSample = 0;
// get file offset for this sample // get file offset for this sample
int32 dwSampleIndex = _sampleIndex[id]; uint32 dwSampleIndex = _sampleIndex[id];
// move to correct position in the sample file // move to correct position in the sample file
_sampleStream.seek(dwSampleIndex); _sampleStream.seek(dwSampleIndex);
if (_sampleStream.ioFailed() || _sampleStream.pos() != dwSampleIndex) if (_sampleStream.ioFailed() || (uint32)_sampleStream.pos() != dwSampleIndex)
error("File %s is corrupt", SAMPLE_FILE); error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// read the length of the sample // read the length of the sample
uint32 sampleLen = _sampleStream.readUint32LE(); uint32 sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed()) if (_sampleStream.ioFailed())
error("File %s is corrupt", SAMPLE_FILE); error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// allocate a buffer // allocate a buffer
void *sampleBuf = malloc(sampleLen); void *sampleBuf = malloc(sampleLen);
@ -92,7 +105,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// read all of the sample // read all of the sample
if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen) if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
error("File %s is corrupt", SAMPLE_FILE); error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// FIXME: Should set this in a different place ;) // FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound); _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound);
@ -101,15 +114,211 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// play it // play it
_vm->_mixer->playRaw(type, &_handle, sampleBuf, sampleLen, 22050, _vm->_mixer->playRaw(type, &curChan.handle, sampleBuf, sampleLen, 22050,
Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED); Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_UNSIGNED);
if (handle) if (handle)
*handle = _handle; *handle = curChan.handle;
return true; return true;
} }
bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int priority,
Audio::Mixer::SoundType type, Audio::SoundHandle *handle) {
// Floppy version has no sample file
if (_vm->getFeatures() & GF_FLOPPY)
return false;
// no sample driver?
if (!_vm->_mixer->isReady())
return false;
Channel *curChan;
uint8 sndVol = 255;
// Sample on screen?
if (!offscreenChecks(x, y))
return false;
// If that sample is already playing, stop it
stopSpecSample(id, sub);
if (type == Audio::Mixer::kSpeechSoundType) {
curChan = &_channels[kChannelTalk];
} else if (type == Audio::Mixer::kSFXSoundType) {
uint32 oldestTime = g_system->getMillis();
int oldestChan = kChannelSFX;
int chan;
for (chan = kChannelSFX; chan < kNumChannels; chan++) {
if (!_vm->_mixer->isSoundHandleActive(_channels[chan].handle))
break;
if ((_channels[chan].lastStart < oldestTime) &&
(_channels[chan].priority <= priority)) {
oldestTime = _channels[chan].lastStart;
oldestChan = chan;
}
}
if (chan == kNumChannels) {
if (_channels[oldestChan].priority > priority) {
warning("playSample: No free channel");
return false;
}
chan = oldestChan;
}
if (_vm->_pcmMusic->isDimmed() && SysVar(SYS_SceneFxDimFactor))
sndVol = 255 - 255/SysVar(SYS_SceneFxDimFactor);
curChan = &_channels[chan];
} else {
warning("playSample: Unknown SoundType");
return false;
}
// stop any currently playing sample
_vm->_mixer->stopHandle(curChan->handle);
// make sure id is in range
assert(id > 0 && id < _sampleIndexLen);
// get file offset for this sample
uint32 dwSampleIndex = _sampleIndex[id];
if (dwSampleIndex == 0) {
warning("Tinsel2 playSample, non-existant sample %d", id);
return false;
}
// move to correct position in the sample file
_sampleStream.seek(dwSampleIndex);
if (_sampleStream.ioFailed() || (uint32)_sampleStream.pos() != dwSampleIndex)
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// read the length of the sample
uint32 sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
if (sampleLen & 0x80000000) {
// Has sub samples
int32 numSubs = sampleLen & ~0x80000000;
assert(sub >= 0 && sub < numSubs);
// Skipping
for (int32 i = 0; i < sub; i++) {
sampleLen = _sampleStream.readUint32LE();
_sampleStream.skip(sampleLen);
if (_sampleStream.ioFailed())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
}
sampleLen = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed())
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
}
debugC(DEBUG_DETAILED, kTinselDebugSound, "Playing sound %d.%d, %d bytes at %d (pan %d)", id, sub, sampleLen,
_sampleStream.pos(), getPan(x));
// allocate a buffer
byte *sampleBuf = (byte *) malloc(sampleLen);
assert(sampleBuf);
// read all of the sample
if (_sampleStream.read(sampleBuf, sampleLen) != sampleLen)
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
Common::MemoryReadStream *sampleStream =
new Common::MemoryReadStream(sampleBuf, sampleLen, true);
Audio::AudioStream *_stream =
makeADPCMStream(sampleStream, true, sampleLen, Audio::kADPCMTinsel6, 22050, 1, 24);
// FIXME: Should set this in a different place ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound);
//_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volVoice);
curChan->sampleNum = id;
curChan->subSample = sub;
curChan->looped = bLooped;
curChan->x = x;
curChan->y = y;
curChan->priority = priority;
curChan->lastStart = g_system->getMillis();
// /---Compression----\ Milis BytesPerSecond
curChan->timeDuration = (((sampleLen * 64) / 25) * 1000) / (22050 * 2);
// Play it
_vm->_mixer->playInputStream(type, &curChan->handle, _stream);
_vm->_mixer->setChannelVolume(curChan->handle, sndVol);
_vm->_mixer->setChannelBalance(curChan->handle, getPan(x));
if (handle)
*handle = curChan->handle;
return true;
}
/**
* Returns FALSE if sample doesn't need playing
*/
bool SoundManager::offscreenChecks(int x, int &y)
{
// No action if no x specification
if (x == -1)
return true;
// convert x to offset from screen centre
x -= PlayfieldGetCentreX(FIELD_WORLD);
if (x < -SCREEN_WIDTH || x > SCREEN_WIDTH) {
// A long way offscreen, ignore it
return false;
} else if (x < -SCREEN_WIDTH/2 || x > SCREEN_WIDTH/2) {
// Off-screen, attennuate it
y = (y > 0) ? (y / 2) : 50;
return true;
} else
return true;
}
int8 SoundManager::getPan(int x) {
if (x == -1)
return 0;
x -= PlayfieldGetCentreX(FIELD_WORLD);
if (x == 0)
return 0;
if (x < 0) {
if (x < (-SCREEN_WIDTH / 2))
return -127;
x = (-x * 127) / (SCREEN_WIDTH / 2);
return 0 - x;
}
if (x > (SCREEN_WIDTH / 2))
return 127;
x = (x * 127) / (SCREEN_WIDTH / 2);
return x;
}
/** /**
* Returns TRUE if there is a sample for the specified sample identifier. * Returns TRUE if there is a sample for the specified sample identifier.
* @param id Identifier of sample to be checked * @param id Identifier of sample to be checked
@ -131,8 +340,16 @@ bool SoundManager::sampleExists(int id) {
/** /**
* Returns true if a sample is currently playing. * Returns true if a sample is currently playing.
*/ */
bool SoundManager::sampleIsPlaying(void) { bool SoundManager::sampleIsPlaying(int id) {
return _vm->_mixer->isSoundHandleActive(_handle); if (!TinselV2)
return _vm->_mixer->isSoundHandleActive(_channels[kChannelTinsel1].handle);
for (int i = 0; i < kNumChannels; i++)
if (_channels[i].sampleNum == id)
if (_vm->_mixer->isSoundHandleActive(_channels[i].handle))
return true;
return false;
} }
/** /**
@ -140,7 +357,37 @@ bool SoundManager::sampleIsPlaying(void) {
*/ */
void SoundManager::stopAllSamples(void) { void SoundManager::stopAllSamples(void) {
// stop currently playing sample // stop currently playing sample
_vm->_mixer->stopHandle(_handle);
if (!TinselV2) {
_vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
return;
}
for (int i = 0; i < kNumChannels; i++)
_vm->_mixer->stopHandle(_channels[i].handle);
}
void SoundManager::stopSpecSample(int id, int sub) {
debugC(DEBUG_DETAILED, kTinselDebugSound, "stopSpecSample(%d, %d)", id, sub);
if (!TinselV2) {
if (_channels[kChannelTinsel1].sampleNum == id)
_vm->_mixer->stopHandle(_channels[kChannelTinsel1].handle);
return;
}
for (int i = kChannelTalk; i < kNumChannels; i++) {
if ((_channels[i].sampleNum == id) && (_channels[i].subSample == sub))
_vm->_mixer->stopHandle(_channels[i].handle);
}
}
void SoundManager::setSFXVolumes(uint8 volume) {
if (!TinselV2)
return;
for (int i = kChannelSFX; i < kNumChannels; i++)
_vm->_mixer->setChannelVolume(_channels[i].handle, volume);
} }
/** /**
@ -158,7 +405,7 @@ void SoundManager::openSampleFiles(void) {
return; return;
// open sample index file in binary mode // open sample index file in binary mode
if (f.open(SAMPLE_INDEX)) { if (f.open(_vm->getSampleIndex(sampleLanguage))) {
// get length of index file // get length of index file
f.seek(0, SEEK_END); // move to end of file f.seek(0, SEEK_END); // move to end of file
_sampleIndexLen = f.pos(); // get file pointer _sampleIndexLen = f.pos(); // get file pointer
@ -166,7 +413,7 @@ void SoundManager::openSampleFiles(void) {
if (_sampleIndex == NULL) { if (_sampleIndex == NULL) {
// allocate a buffer for the indices // allocate a buffer for the indices
_sampleIndex = (int32 *)malloc(_sampleIndexLen); _sampleIndex = (uint32 *)malloc(_sampleIndexLen);
// make sure memory allocated // make sure memory allocated
if (_sampleIndex == NULL) { if (_sampleIndex == NULL) {
@ -179,7 +426,7 @@ void SoundManager::openSampleFiles(void) {
// load data // load data
if (f.read(_sampleIndex, _sampleIndexLen) != (uint32)_sampleIndexLen) if (f.read(_sampleIndex, _sampleIndexLen) != (uint32)_sampleIndexLen)
// file must be corrupt if we get to here // file must be corrupt if we get to here
error("File %s is corrupt", SAMPLE_FILE); error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
#ifdef SCUMM_BIG_ENDIAN #ifdef SCUMM_BIG_ENDIAN
// Convert all ids from LE to native format // Convert all ids from LE to native format
@ -194,18 +441,25 @@ void SoundManager::openSampleFiles(void) {
// convert file size to size in DWORDs // convert file size to size in DWORDs
_sampleIndexLen /= sizeof(uint32); _sampleIndexLen /= sizeof(uint32);
} else } else
error("Cannot find file %s", SAMPLE_INDEX); error(CANNOT_FIND_FILE, _vm->getSampleIndex(sampleLanguage));
// open sample file in binary mode // open sample file in binary mode
if (!_sampleStream.open(SAMPLE_FILE)) if (!_sampleStream.open(_vm->getSampleFile(sampleLanguage)))
error("Cannot find file %s", SAMPLE_FILE); error(CANNOT_FIND_FILE, _vm->getSampleFile(sampleLanguage));
/* /*
// gen length of the largest sample // gen length of the largest sample
sampleBuffer.size = _sampleStream.readUint32LE(); sampleBuffer.size = _sampleStream.readUint32LE();
if (_sampleStream.ioFailed()) if (_sampleStream.ioFailed())
error("File %s is corrupt", SAMPLE_FILE); error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
*/ */
} }
void SoundManager::closeSampleStream(void) {
_sampleStream.close();
free(_sampleIndex);
_sampleIndex = 0;
_sampleIndexLen = 0;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -37,6 +37,11 @@
namespace Tinsel { namespace Tinsel {
enum STYPE {FX, VOICE};
enum PlayPriority { PRIORITY_SCRIPT, PRIORITY_SPLAY1, PRIORITY_SPLAY2, PRIORITY_TALK };
enum SampleFlags {PS_COMPLETE = 0x01, PS_SUSTAIN = 0x02};
/*----------------------------------------------------------------------*\ /*----------------------------------------------------------------------*\
|* Function Prototypes *| |* Function Prototypes *|
@ -44,14 +49,39 @@ namespace Tinsel {
class SoundManager { class SoundManager {
protected: protected:
static const int kNumSFX = 3; // Number of SFX channels
enum {
kChannelTalk = 0,
kChannelTinsel1 = 0, // Always using this channel for DW1
kChannelSFX = 1
};
static const int kNumChannels = kChannelSFX + kNumSFX;
struct Channel {
// Sample handle
Audio::SoundHandle handle;
// Sample id
int sampleNum;
int subSample;
// Playing properties
bool looped;
int x, y;
int priority;
// Time properties
uint32 timeStarted;
uint32 timeDuration;
uint32 lastStart;
};
Channel _channels[kNumChannels];
//TinselEngine *_vm; // TODO: Enable this once global _vm var is gone //TinselEngine *_vm; // TODO: Enable this once global _vm var is gone
/** Sample handle */
Audio::SoundHandle _handle;
/** Sample index buffer and number of entries */ /** Sample index buffer and number of entries */
int32 *_sampleIndex; uint32 *_sampleIndex;
/** Number of entries in the sample index */ /** Number of entries in the sample index */
long _sampleIndexLen; long _sampleIndexLen;
@ -59,19 +89,29 @@ protected:
/** file stream for sample file */ /** file stream for sample file */
Common::File _sampleStream; Common::File _sampleStream;
bool offscreenChecks(int x, int &y);
int8 getPan(int x);
public: public:
SoundManager(TinselEngine *vm); SoundManager(TinselEngine *vm);
~SoundManager(); ~SoundManager();
bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0); bool playSample(int id, Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
bool playSample(int id, int sub, bool bLooped, int x, int y, int priority,
Audio::Mixer::SoundType type, Audio::SoundHandle *handle = 0);
void stopAllSamples(void); // Stops any currently playing sample void stopAllSamples(void); // Stops any currently playing sample
void stopSpecSample(int id, int sub = 0); // Stops a specific sample
void setSFXVolumes(uint8 volume);
bool sampleExists(int id); bool sampleExists(int id);
bool sampleIsPlaying(void); bool sampleIsPlaying(int id = -1);
// TODO: Internal method, make this protected? // TODO: Internal method, make this protected?
void openSampleFiles(void); void openSampleFiles(void);
void closeSampleStream(void);
}; };
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -40,18 +40,39 @@ int newestString;
// buffer for resource strings // buffer for resource strings
static uint8 *textBuffer = 0; static uint8 *textBuffer = 0;
// language resource string filenames static struct {
static const char *languageFiles[] = { bool bPresent;
"english.txt", const char *szStem;
"french.txt", SCNHANDLE hDescription;
"german.txt", SCNHANDLE hFlagFilm;
"italian.txt",
"spanish.txt" } languages[NUM_LANGUAGES] = {
{ false, "English", 0, 0 },
{ false, "French", 0, 0 },
{ false, "German", 0, 0 },
{ false, "Italian", 0, 0 },
{ false, "Spanish", 0, 0 },
{ false, "Hebrew", 0, 0 },
{ false, "Magyar", 0, 0 },
{ false, "Japanese",0, 0 },
{ false, "US", 0, 0 }
}; };
// Set if we're handling 2-byte characters. // Set if we're handling 2-byte characters.
bool bMultiByte = false; bool bMultiByte = false;
LANGUAGE textLanguage, sampleLanguage = TXT_ENGLISH;
//----------------- LOCAL DEFINES ----------------------------
#define languageExtension ".txt"
#define indexExtension ".idx"
#define sampleExtension ".smp"
//----------------- FUNCTIONS --------------------------------
/** /**
* Called to load a resource file for a different language * Called to load a resource file for a different language
* @param newLang The new language * @param newLang The new language
@ -60,6 +81,9 @@ void ChangeLanguage(LANGUAGE newLang) {
Common::File f; Common::File f;
uint32 textLen = 0; // length of buffer uint32 textLen = 0; // length of buffer
textLanguage = newLang;
sampleLanguage = newLang;
if (textBuffer) { if (textBuffer) {
// free the previous buffer // free the previous buffer
free(textBuffer); free(textBuffer);
@ -69,9 +93,9 @@ void ChangeLanguage(LANGUAGE newLang) {
// Try and open the specified language file. If it fails, and the language // Try and open the specified language file. If it fails, and the language
// isn't English, try falling back on opening 'english.txt' - some foreign // isn't English, try falling back on opening 'english.txt' - some foreign
// language versions reused it rather than their proper filename // language versions reused it rather than their proper filename
if (!f.open(languageFiles[newLang])) { if (!f.open(_vm->getTextFile(newLang))) {
if ((newLang == TXT_ENGLISH) || !f.open(languageFiles[TXT_ENGLISH])) if ((newLang == TXT_ENGLISH) || !f.open(_vm->getTextFile(TXT_ENGLISH)))
error("Cannot find file %s", languageFiles[newLang]); error(CANNOT_FIND_FILE, _vm->getTextFile(newLang));
} }
// Check whether the file is compressed or not - for compressed files the // Check whether the file is compressed or not - for compressed files the
@ -79,7 +103,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// identifier // identifier
textLen = f.readUint32LE(); textLen = f.readUint32LE();
if (f.ioFailed()) if (f.ioFailed())
error("File %s is corrupt", languageFiles[newLang]); error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) { if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) {
// the file is uncompressed // the file is uncompressed
@ -101,7 +125,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// load data // load data
if (f.read(textBuffer, textLen) != textLen) if (f.read(textBuffer, textLen) != textLen)
// file must be corrupt if we get to here // file must be corrupt if we get to here
error("File %s is corrupt", languageFiles[newLang]); error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
// close the file // close the file
f.close(); f.close();
@ -111,19 +135,11 @@ void ChangeLanguage(LANGUAGE newLang) {
} }
/** /**
* Loads a string resource identified by id. * FindStringBase
* @param id identifier of string to be loaded
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/ */
int LoadStringRes(int id, char *pBuffer, int bufferMax) { static byte *FindStringBase(int id) {
#ifdef DEBUG
// For diagnostics
newestString = id;
#endif
// base of string resource table // base of string resource table
uint8 *pText = textBuffer; byte *pText = textBuffer;
// index into text resource file // index into text resource file
uint32 index = 0; uint32 index = 0;
@ -134,20 +150,14 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
// number of strings to skip when in the correct chunk // number of strings to skip when in the correct chunk
int strSkip = id % STRINGS_PER_CHUNK; int strSkip = id % STRINGS_PER_CHUNK;
// length of string
int len;
// skip to the correct chunk // skip to the correct chunk
while (chunkSkip-- != 0) { while (chunkSkip-- != 0) {
// make sure chunk id is correct // make sure chunk id is correct
assert(READ_LE_UINT32(pText + index) == CHUNK_STRING || READ_LE_UINT32(pText + index) == CHUNK_MBSTRING); assert(READ_LE_UINT32(pText + index) == CHUNK_STRING || READ_LE_UINT32(pText + index) == CHUNK_MBSTRING);
if (READ_LE_UINT32(pText + index + sizeof(uint32)) == 0) { if (READ_LE_UINT32(pText + index + sizeof(uint32)) == 0) {
// TEMPORARY DIRTY BODGE
strcpy(pBuffer, "!! HIGH STRING !!");
// string does not exist // string does not exist
return 0; return NULL;
} }
// get index to next chunk // get index to next chunk
@ -163,17 +173,114 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
// skip to the correct string // skip to the correct string
while (strSkip-- != 0) { while (strSkip-- != 0) {
// skip to next string // skip to next string
if (!TinselV2 || ((*pText & 0x80) == 0)) {
// Tinsel 1, or string of length < 128
pText += *pText + 1;
} else if (*pText == 0x80) {
// string of length 128 - 255
pText++; // skip control byte
pText += *pText + 1;
} else if (*pText == 0x90) {
// string of length 256 - 511
pText++; // skip control byte
pText += *pText + 1 + 256;
} else { // multiple string
int subCount;
subCount = *pText & ~0x80;
pText++; // skip control byte
// skip prior sub-strings
while (subCount--) {
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
pText += *pText + 1;
} else if (*pText == 0x90) {
pText++;
pText += *pText + 1 + 256;
} else
pText += *pText + 1; pText += *pText + 1;
} }
}
}
return pText;
}
/**
* Loads a string resource identified by id.
* @param id identifier of string to be loaded
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadStringResource(int id, int sub, char *pBuffer, int bufferMax) {
int len; // length of string
byte *pText = FindStringBase(id);
if (pText == NULL) {
strcpy(pBuffer, "!! HIGH STRING !!");
return 0;
}
if (!TinselV2 || ((*pText & 0x80) == 0)) {
// get length of string
len = *pText;
} else if (*pText == 0x80) {
// string of length 128 - 255
pText++; // skip control byte
// get length of string // get length of string
len = *pText; len = *pText;
} else if (*pText == 0x90) {
// string of length 128 - 255
pText++; // skip control byte
if (len) { // get length of string
len = *pText + 256;
} else {
// multiple string
pText++; // skip control byte
// skip prior sub-strings
while (sub--) {
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
pText += *pText + 1;
} else if (*pText == 0x90) {
pText++;
pText += *pText + 1 + 256;
} else
pText += *pText + 1;
}
// skip control byte, if there is one
if (*pText == 0x80) {
pText++;
// get length of string
len = *pText;
} else if (*pText == 0x90) {
pText++;
// get length of string
len = *pText + 256;
} else {
// get length of string
len = *pText;
}
}
if (len)
{
// the string exists // the string exists
// copy the string to the buffer // copy the string to the buffer
if (len < bufferMax) { if (len < bufferMax)
{
memcpy(pBuffer, pText + 1, len); memcpy(pBuffer, pText + 1, len);
// null terminate // null terminate
@ -199,6 +306,42 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
return 0; return 0;
} }
int LoadStringRes(int id, char *pBuffer, int bufferMax) {
return LoadStringResource(id, 0, pBuffer, bufferMax);
}
/**
* Loads a string resource identified by id
* @param id identifier of string to be loaded
* @param sub sub-string number
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadSubString(int id, int sub, char *pBuffer, int bufferMax) {
return LoadStringResource(id, sub, pBuffer, bufferMax);
}
/**
* SubStringCount
* @param id Identifier of string to be tested
*/
int SubStringCount(int id) {
byte *pText;
pText = FindStringBase(id);
if (pText == NULL)
return 0;
if ((*pText & 0x80) == 0 || *pText == 0x80 || *pText == 0x90) {
// string of length < 128 or string of length 128 - 255
// or of length 256 - 511
return 1;
} else
return (*pText & ~0x80);
}
void FreeTextBuffer() { void FreeTextBuffer() {
if (textBuffer) { if (textBuffer) {
free(textBuffer); free(textBuffer);
@ -206,4 +349,81 @@ void FreeTextBuffer() {
} }
} }
/**
* Called from TINLIB.C from DeclareLanguage().
*/
void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm) {
assert(language >= 0 && language < NUM_LANGUAGES);
languages[language].hDescription = hDescription;
languages[language].hFlagFilm = hFlagFilm;
}
/**
* Gets the current subtitles language
*/
LANGUAGE TextLanguage(void) {
return textLanguage;
}
/**
* Gets the current voice language
*/
LANGUAGE SampleLanguage(void) {
return sampleLanguage;
}
int NumberOfLanguages(void) {
int i, count;
for (i = 0, count = 0; i < NUM_LANGUAGES; i++) {
if (languages[i].bPresent)
count++;
}
return count;
}
LANGUAGE NextLanguage(LANGUAGE thisOne) {
int i;
for (i = thisOne+1; i < NUM_LANGUAGES; i++) {
if (languages[i].bPresent)
return (LANGUAGE)i;
}
for (i = 0; i < thisOne; i++) {
if (languages[i].bPresent)
return (LANGUAGE)i;
}
// No others!
return thisOne;
}
LANGUAGE PrevLanguage(LANGUAGE thisOne) {
int i;
for (i = thisOne-1; i >= 0; i--) {
if (languages[i].bPresent)
return (LANGUAGE)i;
}
for (i = NUM_LANGUAGES-1; i > thisOne; i--) {
if (languages[i].bPresent)
return (LANGUAGE)i;
}
// No others!
return thisOne;
}
SCNHANDLE LanguageDesc(LANGUAGE thisOne) {
return languages[thisOne].hDescription;
}
SCNHANDLE LanguageFlag(LANGUAGE thisOne) {
return languages[thisOne].hFlagFilm;
}
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -58,11 +58,44 @@ void ChangeLanguage(LANGUAGE newLang);
*/ */
int LoadStringRes(int id, char *pBuffer, int bufferMax); int LoadStringRes(int id, char *pBuffer, int bufferMax);
/**
* Loads a string resource identified by id
* @param id identifier of string to be loaded
* @param sub sub-string number
* @param pBuffer points to buffer that receives the string
* @param bufferMax maximum number of chars to be copied to the buffer
*/
int LoadSubString(int id, int sub, char *pBuffer, int bufferMax);
int SubStringCount(int id); // identifier of string to be tested
/** /**
* Frees the text buffer allocated from ChangeLanguage() * Frees the text buffer allocated from ChangeLanguage()
*/ */
void FreeTextBuffer(); void FreeTextBuffer();
/**
* Called from TINLIB.C from DeclareLanguage().
*/
void LanguageFacts(int language, SCNHANDLE hDescription, SCNHANDLE hFlagFilm);
/**
* Gets the current subtitles language
*/
LANGUAGE TextLanguage(void);
/**
* Gets the current voice language
*/
LANGUAGE SampleLanguage(void);
int NumberOfLanguages(void);
LANGUAGE NextLanguage(LANGUAGE thisOne);
LANGUAGE PrevLanguage(LANGUAGE thisOne);
SCNHANDLE LanguageDesc(LANGUAGE thisOne);
SCNHANDLE LanguageFlag(LANGUAGE thisOne);
} // end of namespace Tinsel } // end of namespace Tinsel
#endif #endif

218
engines/tinsel/sysvar.cpp Normal file
View file

@ -0,0 +1,218 @@
/* 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$
*
* System variable handling.
*/
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/dialogs.h"
#include "tinsel/strres.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
// Return for SYS_Platform
typedef enum { DOS_PC, WIN_PC, APPLE_MAC, SONY_PSX, SEGA_SATURN } platform;
//----------------- GLOBAL GLOBAL DATA --------------------
// To prevent assembler from needing to call SysVar()
uint8 ghostColour;
extern int NewestSavedGame(void);
//----------------- LOCAL GLOBAL DATA --------------------
static int systemVars[SV_TOPVALID] = {
INV_1, // Default inventory
10, // Y-offset of Conversation(TOP)
320, // Y-offset of Conversation(BOT)
15, // Minimum distance from side
10, // Minimum distance from top
115, // Distance above actor
10, // Distance below actor
0, // Current language **READ ONLY**
0, // Sample language **READ ONLY**
0, // Current state **READ ONLY**
0, // Saved Game Exists **READ ONLY**
true, // Should Conversation() wait for scroll? [TRUE]
true, // Should Talk()/Say() wait for scroll? [TRUE]
true, // Enable PointTag()
true, // Enable cursor with PrintCursor()
100, // SV_SCROLL_XTRIGGER
0, // SV_SCROLL_XDISTANCE
16, // SV_SCROLL_XSPEED
40, // SV_SCROLL_YTRIGGERTOP
40, // SV_SCROLL_YTRIGGERBOT
0, // SV_SCROLL_YDISTANCE
16, // SV_SCROLL_YSPEED
2, // Speech Delay
2, // Music dim factor
0, // if set, default actor's text colour gets poked in here
0, // user 1
0, // user 2
0, // user 3
0, // user 4
0, // user 5
0, // user 6
0, // SYS_MinimumXoffset
0, // SYS_MaximumXoffset
0, // SYS_MinimumYoffset
0, // SYS_MaximumYoffset
0, // SYS_DefaultFxDimFactor
0, // SYS_SceneFxDimFactor
0x606060, // SYS_HighlightRGB
WIN_PC, // SYS_Platform,
0, // SYS_Debug
0, // ISV_DIVERT_ACTOR
false, // ISV_NO_BLOCKING
0, // ISV_GHOST_ACTOR
0, // ISV_GHOST_BASE
0 // ISV_GHOST_COLOUR
};
static SCNHANDLE systemStrings[SS_MAX_VALID];
//static bool bFlagNoBlocking = false;
//----------------- FUNCTIONS --------------------------------
/**
* Initialises the system variable list
*/
void InitSysVars() {
systemVars[SV_SCROLL_XDISTANCE] = SCREEN_WIDTH / 2;
systemVars[SV_SCROLL_YDISTANCE] = SCREEN_BOX_HEIGHT1 / 2;
}
/**
* SetSysVar
*/
void SetSysVar(int varId, int newValue) {
if (varId < 0 || varId >= SV_TOPVALID)
error("SetSystemVar(): out of range identifier");
switch (varId) {
case SV_LANGUAGE:
case SV_SAMPLE_LANGUAGE:
case SV_SUBTITLES:
case SV_SAVED_GAME_EXISTS:
case SYS_Platform:
case SYS_Debug:
error("SetSystemVar(): read only identifier");
default:
systemVars[varId] = newValue;
if (varId == ISV_GHOST_COLOUR) {
ghostColour = (uint8)newValue;
}
}
}
int SysVar(int varId) {
if (varId < 0 || varId >= SV_TOPVALID)
error("SystemVar(): out of range identifier");
switch (varId) {
case SV_LANGUAGE:
return TextLanguage();
case SV_SAMPLE_LANGUAGE:
return SampleLanguage();
case SV_SUBTITLES:
// FIXME: This isn't currently defined
return false;
//return bSubtitles;
case SV_SAVED_GAME_EXISTS:
return NewestSavedGame() != -1;
case SYS_Debug:
// FIXME: This isn't currently defined
return false;
//return bDebuggingAllowed;
default:
return systemVars[varId];
}
}
void SaveSysVars(int *pSv) {
memcpy(pSv, systemVars, sizeof(systemVars));
}
void RestoreSysVars(int *pSv) {
memcpy(systemVars, pSv, sizeof(systemVars));
ghostColour = (uint8)SysVar(ISV_GHOST_COLOUR);
}
void SetSysString(int number, SCNHANDLE hString) {
assert(number >= 0 && number < SS_MAX_VALID);
systemStrings[number] = hString;
}
SCNHANDLE SysString(int number) {
assert(number >= 0 && number < SS_MAX_VALID);
return systemStrings[number];
}
/**
* Gets the no blocking flag. Note that for convenience, the systemVars arrray entry is
* used even for Tinsel 1, which used a separate boolean variable
*/
bool GetNoBlocking(void) {
return SysVar(ISV_NO_BLOCKING);
}
/**
* Sets the no blocking flag. Note that for convenience, the systemVars arrray entry is
* used even for Tinsel 1, which used a separate boolean variable
*/
void SetNoBlocking(bool flag) {
SetSysVar(ISV_NO_BLOCKING, flag);
}
} // end of namespace Tinsel

149
engines/tinsel/sysvar.h Normal file
View file

@ -0,0 +1,149 @@
/* 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$
*
* System variable handling.
*/
#ifndef TINSEL_SYSVAR_H // prevent multiple includes
#define TINSEL_SYSVAR_H
namespace Tinsel {
typedef enum { SV_DEFAULT_INV,
SV_CONV_TOPY, // Y-offset of Conversation(TOP)
SV_CONV_BOTY, // Y-offset of Conversation(BOT)
SV_CONV_MINX, // Minimum distance from side
SV_CONV_MINY, // Minimum distance from top
SV_CONV_ABOVE_Y, // Distance above actor
SV_CONV_BELOW_Y, // Distance below actor
SV_LANGUAGE,
SV_SAMPLE_LANGUAGE,
SV_SUBTITLES,
SV_SAVED_GAME_EXISTS,
SV_CONVERSATIONWAITS, // } Do they wait for
SV_SPEECHWAITS, // } scrolls to complete?
SV_ENABLEPOINTTAG, // Enable PointTag()
SV_ENABLEPRINTCURSOR, // Enable cursor with PrintCursor()
SV_SCROLL_XTRIGGER, // }
SV_SCROLL_XDISTANCE, // }
SV_SCROLL_XSPEED, // } Scroll parameters!
SV_SCROLL_YTRIGGERTOP, // }
SV_SCROLL_YTRIGGERBOT, // }
SV_SCROLL_YDISTANCE, // }
SV_SCROLL_YSPEED, // }
SV_SPEECHDELAY, // Delay 'twixt text/animation and sample
SV_MUSICDIMFACTOR, // dimVolume = volume - volume/SV_MDF
SV_TAGCOLOUR, // if set, default actor's text colour gets poked in here
SV_USER1,
SV_USER2,
SV_USER3,
SV_USER4,
SV_USER5,
SV_USER6,
SV_MinimumXoffset,
SV_MaximumXoffset,
SV_MinimumYoffset,
SV_MaximumYoffset,
// dimVolume = volume - volume/DF
SYS_DefaultFxDimFactor, // To this at start of scene
SYS_SceneFxDimFactor, // Alter within scene
SYS_HighlightRGB,
SYS_Platform, // Hardware platform **READ ONLY**
SYS_Debug, // TRUE for debug build/'cheat'**READ ONLY**
ISV_DIVERT_ACTOR,
ISV_NO_BLOCKING,
ISV_GHOST_ACTOR,
ISV_GHOST_BASE,
ISV_GHOST_COLOUR,
SV_TOPVALID } SYSVARS;
typedef enum {
// Main Menu
SS_LOAD_OPTION, //
SS_SAVE_OPTION, //
SS_RESTART_OPTION, //
SS_SOUND_OPTION, //
SS_CONTROL_OPTION, //
SS_SUBTITLES_OPTION, //
SS_QUIT_OPTION, //
SS_RESUME_OPTION, //
SS_LOAD_HEADING,
SS_SAVE_HEADING,
SS_RESTART_HEADING,
SS_QUIT_HEADING,
SS_MVOL_SLIDER,
SS_SVOL_SLIDER,
SS_VVOL_SLIDER,
SS_DCLICK_SLIDER,
SS_DCLICK_TEST,
SS_SWAP_TOGGLE,
SS_TSPEED_SLIDER,
SS_STITLE_TOGGLE,
SS_HOPPER1, // Hopper scene menu heading
SS_SOUND_HEADING,
SS_CONTROLS_HEADING,
SS_LANGUAGE_SELECT,
SS_MAX_VALID
} BOLLOX;
void InitSysVars();
void SetSysVar(int varId, int newValue);
int SysVar(int varId);
void SaveSysVars(int *pSv);
void RestoreSysVars(int *pSv);
void SetSysString(int number, SCNHANDLE hString);
SCNHANDLE SysString(int number);
bool GetNoBlocking(void);
void SetNoBlocking(bool flag);
} // end of namespace Tinsel
#endif

View file

@ -105,9 +105,10 @@ int JustifyText(char *szStr, int xPos, const FONT *pFont, int mode) {
* @param yPos Y position of string * @param yPos Y position of string
* @param hFont Which font to use * @param hFont Which font to use
* @param mode Mode flags for the string * @param mode Mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/ */
OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos, OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
SCNHANDLE hFont, int mode) { int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification int xJustify; // x position of text after justification
int yOffset; // offset to next line of text int yOffset; // offset to next line of text
OBJECT *pFirst; // head of multi-object text list OBJECT *pFirst; // head of multi-object text list
@ -130,7 +131,7 @@ OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos
pImg = (const IMAGE *)LockMem(FROM_LE_32(pFont->fontDef[(int)'W'])); pImg = (const IMAGE *)LockMem(FROM_LE_32(pFont->fontDef[(int)'W']));
// get height of capital W for offset to next line // get height of capital W for offset to next line
yOffset = FROM_LE_16(pImg->imgHeight); yOffset = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK;
while (*szStr) { while (*szStr) {
// x justify the text according to the mode flags // x justify the text according to the mode flags
@ -175,7 +176,7 @@ OBJECT *ObjectTextOut(OBJECT *pList, char *szStr, int colour, int xPos, int yPos
// fill in character object // fill in character object
pChar->hImg = hImg; // image def pChar->hImg = hImg; // image def
pChar->width = FROM_LE_16(pImg->imgWidth); // width of chars bitmap pChar->width = FROM_LE_16(pImg->imgWidth); // width of chars bitmap
pChar->height = FROM_LE_16(pImg->imgHeight); // height of chars bitmap pChar->height = FROM_LE_16(pImg->imgHeight) & ~C16_FLAG_MASK; // height of chars bitmap
pChar->hBits = FROM_LE_32(pImg->hImgBits); // bitmap pChar->hBits = FROM_LE_32(pImg->hImgBits); // bitmap
// check for absolute positioning // check for absolute positioning

View file

@ -27,6 +27,7 @@
#ifndef TINSEL_TEXT_H // prevent multiple includes #ifndef TINSEL_TEXT_H // prevent multiple includes
#define TINSEL_TEXT_H #define TINSEL_TEXT_H
#include "tinsel/coroutine.h"
#include "tinsel/object.h" // object manager defines #include "tinsel/object.h" // object manager defines
namespace Tinsel { namespace Tinsel {
@ -42,6 +43,10 @@ enum {
/** maximum number of characters in a font */ /** maximum number of characters in a font */
#define MAX_FONT_CHARS 256 #define MAX_FONT_CHARS 256
#define C16_240 0x4000
#define C16_224 0x8000
#define C16_MAP 0xC000
#define C16_FLAG_MASK (C16_240 | C16_224 | C16_MAP)
#include "common/pack-start.h" // START STRUCT PACKING #include "common/pack-start.h" // START STRUCT PACKING
@ -80,14 +85,21 @@ struct TEXTOUT {
|* Text Function Prototypes *| |* Text Function Prototypes *|
\*----------------------------------------------------------------------*/ \*----------------------------------------------------------------------*/
OBJECT *ObjectTextOut( // output a string of text /**
OBJECT *pList, // object list to add text to * Main text outputting routine. If a object list is specified a
char *szStr, // string to output * multi-object is created for the whole text and a pointer to the head
int colour, // colour for monochrome text * of the list is returned.
int xPos, // x position of string * @param pList object list to add text to
int yPos, // y position of string * @param szStr string to output
SCNHANDLE hFont, // which font to use * @param colour colour for monochrome text
int mode); // mode flags for the string * @param xPos x position of string
* @param yPos y position of string
* @param hFont which font to use
* @param mode mode flags for the string
* @param sleepTime Sleep time between each character (if non-zero)
*/
OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime = 0);
OBJECT *ObjectTextOutIndirect( // output a string of text OBJECT *ObjectTextOutIndirect( // output a string of text
TEXTOUT *pText); // pointer to TextOut struct with all parameters TEXTOUT *pText); // pointer to TextOut struct with all parameters

View file

@ -151,7 +151,7 @@ void FettleTimers(void) {
/** /**
* Start a timer up. * Start a timer up.
*/ */
void DwSetTimer(int num, int sval, bool up, bool frame) { void StartTimer(int num, int sval, bool up, bool frame) {
TIMER *pt; TIMER *pt;
assert(num); // zero is not allowed as a timer number assert(num); // zero is not allowed as a timer number

View file

@ -44,7 +44,7 @@ void syncTimerInfo(Serializer &s);
void FettleTimers(void); void FettleTimers(void);
void DwSetTimer(int num, int sval, bool up, bool frame); void StartTimer(int num, int sval, bool up, bool frame);
int Timer(int num); int Timer(int num);

File diff suppressed because it is too large Load diff

View file

@ -28,13 +28,40 @@
#define TINSEL_TINLIB_H #define TINSEL_TINLIB_H
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/object.h"
#include "tinsel/palette.h"
namespace Tinsel { namespace Tinsel {
enum EXTREME {
EX_USEXY, EX_BOTTOM, EX_BOTTOMLEFT,
EX_BOTTOMRIGHT, EX_LEFT, EX_RIGHT,
EX_TOP, EX_TOPLEFT, EX_TOPRIGHT
};
enum WHICH_VER {VER_GLITTER, VER_COMPILE};
#define VER_LEN 10
// Support functions
void TinGetVersion(WHICH_VER which, char *buffer, int length);
// Library functions in TINLIB.C // Library functions in TINLIB.C
void control(int param); void ActorBrightness(int actor, int brightness);
void stand(int actor, int x, int y, SCNHANDLE film); void ActorPalette(int actor, int startColour, int length);
void Control(int param);
void HookScene(SCNHANDLE scene, int entrance, int transition);
void NewScene(CORO_PARAM, SCNHANDLE scene, int entrance, int transition);
void Offset(EXTREME extreme, int x, int y);
void RestoreScene(void);
void ResumeLastGame(void);
void SaveScene(CORO_PARAM);
void Stand(CORO_PARAM, int actor, int x, int y, SCNHANDLE film);
void SetTextPal(COLORREF col);
void KeepOnScreen(OBJECT *pText, int *pTextX, int *pTextY);
enum SPEECH_TYPE { IS_SAY, IS_SAYAT, IS_TALK, IS_TALKAT };
} // end of namespace Tinsel } // end of namespace Tinsel

View file

@ -24,6 +24,7 @@
*/ */
#include "common/endian.h" #include "common/endian.h"
#include "common/error.h"
#include "common/events.h" #include "common/events.h"
#include "common/keyboard.h" #include "common/keyboard.h"
#include "common/file.h" #include "common/file.h"
@ -44,13 +45,15 @@
#include "tinsel/background.h" #include "tinsel/background.h"
#include "tinsel/config.h" #include "tinsel/config.h"
#include "tinsel/cursor.h" #include "tinsel/cursor.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h" #include "tinsel/dw.h"
#include "tinsel/events.h" #include "tinsel/events.h"
#include "tinsel/faders.h" #include "tinsel/faders.h"
#include "tinsel/film.h" #include "tinsel/film.h"
#include "tinsel/handle.h" #include "tinsel/handle.h"
#include "tinsel/heapmem.h" // MemoryInit #include "tinsel/heapmem.h" // MemoryInit
#include "tinsel/inventory.h" #include "tinsel/dialogs.h"
#include "tinsel/mareels.h"
#include "tinsel/music.h" #include "tinsel/music.h"
#include "tinsel/object.h" #include "tinsel/object.h"
#include "tinsel/pid.h" #include "tinsel/pid.h"
@ -60,6 +63,7 @@
#include "tinsel/serializer.h" #include "tinsel/serializer.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"
#include "tinsel/strres.h" #include "tinsel/strres.h"
#include "tinsel/sysvar.h"
#include "tinsel/timers.h" #include "tinsel/timers.h"
#include "tinsel/tinsel.h" #include "tinsel/tinsel.h"
@ -71,6 +75,14 @@ namespace Tinsel {
extern void SetDoFadeIn(bool tf); extern void SetDoFadeIn(bool tf);
extern void DropBackground(void); extern void DropBackground(void);
// In BMV.CPP
extern void FettleBMV(void);
extern bool MoviePlaying(void);
extern void CopyMovieToScreen(void);
extern void FinishBMV();
extern int32 MovieAudioLag();
extern uint32 NextMovieTime();
// In CURSOR.CPP // In CURSOR.CPP
extern void CursorProcess(CORO_PARAM, const void *); extern void CursorProcess(CORO_PARAM, const void *);
@ -79,7 +91,6 @@ extern void InventoryProcess(CORO_PARAM, const void *);
// In SCENE.CPP // In SCENE.CPP
extern void PrimeBackground(); extern void PrimeBackground();
extern void NewScene(SCNHANDLE scene, int entry);
extern SCNHANDLE GetSceneHandle(void); extern SCNHANDLE GetSceneHandle(void);
// In TIMER.CPP // In TIMER.CPP
@ -94,6 +105,12 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition);
bool bRestart = false; bool bRestart = false;
bool bHasRestarted = false; bool bHasRestarted = false;
static bool bCuttingScene = false;
static bool bChangingForRestore = false;
static Common::Point clickPos;
#ifdef DEBUG #ifdef DEBUG
bool bFast; // set to make it go ludicrously fast bool bFast; // set to make it go ludicrously fast
#endif #endif
@ -110,11 +127,11 @@ static Scene NextScene = { 0, 0, 0 };
static Scene HookScene = { 0, 0, 0 }; static Scene HookScene = { 0, 0, 0 };
static Scene DelayedScene = { 0, 0, 0 }; static Scene DelayedScene = { 0, 0, 0 };
static bool bHookSuspend = false;
static PROCESS *pMouseProcess = 0; static PROCESS *pMouseProcess = 0;
static PROCESS *pKeyboardProcess = 0; static PROCESS *pKeyboardProcess = 0;
static SCNHANDLE hCdChangeScene;
// Stack of pending mouse button events // Stack of pending mouse button events
Common::List<Common::EventType> mouseButtons; Common::List<Common::EventType> mouseButtons;
@ -142,6 +159,7 @@ void KeyboardProcess(CORO_PARAM, const void *) {
// Get the next keyboard event off the stack // Get the next keyboard event off the stack
Common::Event evt = *keypresses.begin(); Common::Event evt = *keypresses.begin();
keypresses.erase(keypresses.begin()); keypresses.erase(keypresses.begin());
const Common::Point mousePos = _vm->getMousePosition();
// Switch for special keys // Switch for special keys
switch (evt.kbd.keycode) { switch (evt.kbd.keycode) {
@ -150,21 +168,21 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_RALT: case Common::KEYCODE_RALT:
if (evt.type == Common::EVENT_KEYDOWN) { if (evt.type == Common::EVENT_KEYDOWN) {
if (!bSwapButtons) if (!bSwapButtons)
ProcessButEvent(BE_RDSTART); ProcessButEvent(PLR_DRAG2_START);
else else
ProcessButEvent(BE_LDSTART); ProcessButEvent(PLR_DRAG1_START);
} else { } else {
if (!bSwapButtons) if (!bSwapButtons)
ProcessButEvent(BE_LDEND); ProcessButEvent(PLR_DRAG1_END);
else else
ProcessButEvent(BE_RDEND); ProcessButEvent(PLR_DRAG2_END);
} }
continue; continue;
case Common::KEYCODE_LCTRL: case Common::KEYCODE_LCTRL:
case Common::KEYCODE_RCTRL: case Common::KEYCODE_RCTRL:
if (evt.type == Common::EVENT_KEYDOWN) { if (evt.type == Common::EVENT_KEYDOWN) {
ProcessKeyEvent(LOOK_KEY); ProcessKeyEvent(PLR_LOOK);
} else { } else {
// Control key release // Control key release
} }
@ -186,42 +204,42 @@ void KeyboardProcess(CORO_PARAM, const void *) {
switch (evt.kbd.keycode) { switch (evt.kbd.keycode) {
/*** SPACE = WALKTO ***/ /*** SPACE = WALKTO ***/
case Common::KEYCODE_SPACE: case Common::KEYCODE_SPACE:
ProcessKeyEvent(WALKTO_KEY); ProcessKeyEvent(PLR_WALKTO);
continue; continue;
/*** RETURN = ACTION ***/ /*** RETURN = ACTION ***/
case Common::KEYCODE_RETURN: case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER: case Common::KEYCODE_KP_ENTER:
ProcessKeyEvent(ACTION_KEY); ProcessKeyEvent(PLR_ACTION);
continue; continue;
/*** l = LOOK ***/ /*** l = LOOK ***/
case Common::KEYCODE_l: // LOOK case Common::KEYCODE_l: // LOOK
ProcessKeyEvent(LOOK_KEY); ProcessKeyEvent(PLR_LOOK);
continue; continue;
case Common::KEYCODE_ESCAPE: case Common::KEYCODE_ESCAPE:
// WORKAROUND: Check if any of the starting logo screens are active, and if so if (!TinselV2) {
// manually skip to the title screen, allowing them to be bypassed // WORKAROUND: For Discworld 1, check if any of the starting logo screens are
{ // active, and if so manually skip to the title screen, allowing them to be bypassed
int sceneOffset = (_vm->getFeatures() & GF_SCNFILES) ? 1 : 0; int sceneOffset = (_vm->getFeatures() & GF_SCNFILES) ? 1 : 0;
int sceneNumber = (GetSceneHandle() >> SCNHANDLE_SHIFT) - sceneOffset; int sceneNumber = (GetSceneHandle() >> SCNHANDLE_SHIFT) - sceneOffset;
#if 0 // FIXME: Disabled this code for now, as it doesn't work as it should (see bug #2078922).
if ((g_language == TXT_GERMAN) && if ((g_language == TXT_GERMAN) &&
((sceneNumber >= 25 && sceneNumber <= 27) || (sceneNumber == 17))) { ((sceneNumber >= 25 && sceneNumber <= 27) || (sceneNumber == 17))) {
// Skip to title screen // Skip to title screen
// It seems the German CD version uses scenes 25,26,27,17 for the intro, // It seems the German CD version uses scenes 25,26,27,17 for the intro,
// instead of 13,14,15,11; also, the title screen is 11 instead of 10 // instead of 13,14,15,11; also, the title screen is 11 instead of 10
SetNewScene((11 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT); SetNewScene((11 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
} else } else if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
#endif
if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
// Skip to title screen // Skip to title screen
SetNewScene((10 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT); SetNewScene((10 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
} else { } else {
// Not on an intro screen, so process the key normally // Not on an intro screen, so process the key normally
ProcessKeyEvent(ESC_KEY); ProcessKeyEvent(PLR_ESCAPE);
} }
} else {
// Running Discworld 2, so process the key normally
ProcessKeyEvent(PLR_ESCAPE);
} }
continue; continue;
@ -236,48 +254,79 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_F1: case Common::KEYCODE_F1:
// Options dialog // Options dialog
ProcessKeyEvent(OPTION_KEY); ProcessKeyEvent(PLR_MENU);
continue; continue;
case Common::KEYCODE_F5: case Common::KEYCODE_F5:
// Save game // Save game
ProcessKeyEvent(SAVE_KEY); ProcessKeyEvent(PLR_SAVE);
continue; continue;
case Common::KEYCODE_F7: case Common::KEYCODE_F7:
// Load game // Load game
ProcessKeyEvent(LOAD_KEY); ProcessKeyEvent(PLR_LOAD);
continue; continue;
case Common::KEYCODE_m:
// Debug facility - scene hopper
if (TinselV2 && (evt.kbd.flags == Common::KBD_ALT))
ProcessKeyEvent(PLR_JUMP);
break;
case Common::KEYCODE_q: case Common::KEYCODE_q:
if ((evt.kbd.flags == Common::KBD_CTRL) || (evt.kbd.flags == Common::KBD_ALT)) if ((evt.kbd.flags == Common::KBD_CTRL) || (evt.kbd.flags == Common::KBD_ALT))
ProcessKeyEvent(QUIT_KEY); ProcessKeyEvent(PLR_QUIT);
continue; continue;
case Common::KEYCODE_PAGEUP: case Common::KEYCODE_PAGEUP:
case Common::KEYCODE_KP9: case Common::KEYCODE_KP9:
ProcessKeyEvent(PGUP_KEY); ProcessKeyEvent(PLR_PGUP);
continue; continue;
case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_PAGEDOWN:
case Common::KEYCODE_KP3: case Common::KEYCODE_KP3:
ProcessKeyEvent(PGDN_KEY); ProcessKeyEvent(PLR_PGDN);
continue; continue;
case Common::KEYCODE_HOME: case Common::KEYCODE_HOME:
case Common::KEYCODE_KP7: case Common::KEYCODE_KP7:
ProcessKeyEvent(HOME_KEY); ProcessKeyEvent(PLR_HOME);
continue; continue;
case Common::KEYCODE_END: case Common::KEYCODE_END:
case Common::KEYCODE_KP1: case Common::KEYCODE_KP1:
ProcessKeyEvent(END_KEY); ProcessKeyEvent(PLR_END);
continue; continue;
default: default:
ProcessKeyEvent(NOEVENT_KEY); ProcessKeyEvent(PLR_NOEVENT);
break; break;
} }
} }
CORO_END_CODE; CORO_END_CODE;
} }
/**
* Handles launching a single click action result if the timeout for a double-click
* expires
*/
static void SingleLeftProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CONTEXT;
uint32 endTicks;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// Work out when to wait until
_ctx->endTicks = DwGetCurrentTime() + (uint32)dclickSpeed;
// Timeout a double click (may not work once every 49 days!)
do {
CORO_SLEEP(1);
} while(DwGetCurrentTime() < _ctx->endTicks);
if (GetProvNotProcessed())
PlayerEvent(PLR_WALKTO, clickPos);
CORO_KILL_SELF();
CORO_END_CODE;
}
/** /**
* Process to handle changes in the mouse buttons. * Process to handle changes in the mouse buttons.
*/ */
void MouseProcess(CORO_PARAM, const void *) { static void MouseProcess(CORO_PARAM, const void *) {
// COROUTINE // COROUTINE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
bool lastLWasDouble; bool lastLWasDouble;
@ -292,9 +341,6 @@ void MouseProcess(CORO_PARAM, const void *) {
_ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime(); _ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime();
while (true) { while (true) {
// FIXME: I'm still keeping the ctrl/Alt handling in the ProcessKeyEvent method.
// Need to make sure that this works correctly
//DragKeys();
if (mouseButtons.empty()) { if (mouseButtons.empty()) {
// allow scheduling // allow scheduling
@ -306,23 +352,46 @@ void MouseProcess(CORO_PARAM, const void *) {
Common::EventType type = *mouseButtons.begin(); Common::EventType type = *mouseButtons.begin();
mouseButtons.erase(mouseButtons.begin()); mouseButtons.erase(mouseButtons.begin());
int xp, yp;
GetCursorXYNoWait(&xp, &yp, true);
const Common::Point mousePos(xp, yp);
switch (type) { switch (type) {
case Common::EVENT_LBUTTONDOWN: case Common::EVENT_LBUTTONDOWN:
// left button press // left button press
if (DwGetCurrentTime() - _ctx->lastLeftClick < (uint32)dclickSpeed) { if (DwGetCurrentTime() - _ctx->lastLeftClick < (uint32)dclickSpeed) {
// Left button double-click
if (TinselV2) {
// Kill off the button process and fire off the action command
g_scheduler->killMatchingProcess(PID_BTN_CLICK, -1);
PlayerEvent(PLR_ACTION, clickPos);
} else {
// signal left drag start // signal left drag start
ProcessButEvent(BE_LDSTART); ProcessButEvent(PLR_DRAG1_START);
// signal left double click event // signal left double click event
ProcessButEvent(BE_DLEFT); ProcessButEvent(PLR_DLEFT);
}
_ctx->lastLWasDouble = true; _ctx->lastLWasDouble = true;
} else {
// Initial mouse down - either for a single click, or potentially
// the start of a double-click action
if (TinselV2) {
PlayerEvent(PLR_DRAG1_START, mousePos);
ProvNotProcessed();
PlayerEvent(PLR_PROV_WALKTO, mousePos);
} else { } else {
// signal left drag start // signal left drag start
ProcessButEvent(BE_LDSTART); ProcessButEvent(PLR_DRAG1_START);
// signal left single click event // signal left single click event
ProcessButEvent(BE_SLEFT); ProcessButEvent(PLR_SLEFT);
}
_ctx->lastLWasDouble = false; _ctx->lastLWasDouble = false;
} }
@ -332,32 +401,53 @@ void MouseProcess(CORO_PARAM, const void *) {
// left button release // left button release
// update click timer // update click timer
if (_ctx->lastLWasDouble == false) if (_ctx->lastLWasDouble == false) {
_ctx->lastLeftClick = DwGetCurrentTime(); _ctx->lastLeftClick = DwGetCurrentTime();
else
// If player control is enabled, start a process which, if it times out,
// will activate a single button click
if (TinselV2 && ControlIsOn()) {
clickPos = mousePos;
g_scheduler->createProcess(PID_BTN_CLICK, SingleLeftProcess, NULL, 0);
}
} else
_ctx->lastLeftClick -= dclickSpeed; _ctx->lastLeftClick -= dclickSpeed;
if (TinselV2)
// Signal left drag end
PlayerEvent(PLR_DRAG1_END, mousePos);
else
// signal left drag end // signal left drag end
ProcessButEvent(BE_LDEND); ProcessButEvent(PLR_DRAG1_END);
break; break;
case Common::EVENT_RBUTTONDOWN: case Common::EVENT_RBUTTONDOWN:
// right button press // right button press
if (DwGetCurrentTime() - _ctx->lastRightClick < (uint32)dclickSpeed) { if (DwGetCurrentTime() - _ctx->lastRightClick < (uint32)dclickSpeed) {
// Right button double-click
if (TinselV2) {
PlayerEvent(PLR_NOEVENT, clickPos);
} else {
// signal right drag start // signal right drag start
ProcessButEvent(BE_RDSTART); ProcessButEvent(PLR_DRAG2_START);
// signal right double click event // signal right double click event
ProcessButEvent(BE_DRIGHT); ProcessButEvent(PLR_DRIGHT);
}
_ctx->lastRWasDouble = true; _ctx->lastRWasDouble = true;
} else {
if (TinselV2) {
PlayerEvent(PLR_DRAG2_START, mousePos);
PlayerEvent(PLR_LOOK, mousePos);
} else { } else {
// signal right drag start // signal right drag start
ProcessButEvent(BE_RDSTART); ProcessButEvent(PLR_DRAG2_START);
// signal right single click event // signal right single click event
ProcessButEvent(BE_SRIGHT); ProcessButEvent(PLR_SRIGHT);
}
_ctx->lastRWasDouble = false; _ctx->lastRWasDouble = false;
} }
@ -372,8 +462,12 @@ void MouseProcess(CORO_PARAM, const void *) {
else else
_ctx->lastRightClick -= dclickSpeed; _ctx->lastRightClick -= dclickSpeed;
if (TinselV2)
// Signal left drag end
PlayerEvent(PLR_DRAG2_END, mousePos);
else
// signal right drag end // signal right drag end
ProcessButEvent(BE_RDEND); ProcessButEvent(PLR_DRAG2_END);
break; break;
default: default:
@ -402,9 +496,24 @@ static void MasterScriptProcess(CORO_PARAM, const void *) {
/** /**
* Store the facts pertaining to a scene change. * Store the facts pertaining to a scene change.
*/ */
void SetNewScene(SCNHANDLE scene, int entrance, int transition) { void SetNewScene(SCNHANDLE scene, int entrance, int transition) {
if (HookScene.scene == 0 || bHookSuspend) { if (!bCuttingScene && TinselV2)
WrapScene();
// If CD change will be required, stick in the scene change scene
if (CdNumber(scene) != GetCurrentCD()) {
// This scene gets delayed
DelayedScene.scene = scene;
DelayedScene.entry = entrance;
DelayedScene.trans = transition;
NextScene.scene = hCdChangeScene;
NextScene.entry = CdNumber(scene) - '0';
NextScene.trans = TRANS_FADE;
return;
}
if (HookScene.scene == 0 || bCuttingScene) {
// This scene comes next // This scene comes next
NextScene.scene = scene; NextScene.scene = scene;
NextScene.entry = entrance; NextScene.entry = entrance;
@ -424,6 +533,9 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition) {
} }
} }
/**
* Store a scene as hooked
*/
void SetHookScene(SCNHANDLE scene, int entrance, int transition) { void SetHookScene(SCNHANDLE scene, int entrance, int transition) {
assert(HookScene.scene == 0); // scene already hooked assert(HookScene.scene == 0); // scene already hooked
@ -432,6 +544,9 @@ void SetHookScene(SCNHANDLE scene, int entrance, int transition) {
HookScene.trans = transition; HookScene.trans = transition;
} }
/**
* Hooked scene is over, trigger a change to the delayed scene
*/
void UnHookScene(void) { void UnHookScene(void) {
assert(DelayedScene.scene != 0); // no scene delayed assert(DelayedScene.scene != 0); // no scene delayed
@ -444,11 +559,40 @@ void UnHookScene(void) {
} }
void SuspendHook(void) { void SuspendHook(void) {
bHookSuspend = true; bCuttingScene = true;
}
void CdHasChanged(void) {
if (bChangingForRestore) {
bChangingForRestore = false;
RestoreGame(-2);
} else {
assert(DelayedScene.scene != 0);
WrapScene();
// The delayed scene can go now
NextScene.scene = DelayedScene.scene;
NextScene.entry = DelayedScene.entry;
NextScene.trans = DelayedScene.trans;
DelayedScene.scene = 0;
}
}
void SetCdChangeScene(SCNHANDLE hScene) {
hCdChangeScene = hScene;
}
void CDChangeForRestore(int cdNumber) {
NextScene.scene = hCdChangeScene;
NextScene.entry = cdNumber;
NextScene.trans = TRANS_FADE;
bChangingForRestore = true;
} }
void UnSuspendHook(void) { void UnSuspendHook(void) {
bHookSuspend = false; bCuttingScene = false;
} }
void syncSCdata(Serializer &s) { void syncSCdata(Serializer &s) {
@ -468,16 +612,23 @@ static void RestoredProcess(CORO_PARAM, const void *param) {
// COROUTINE // COROUTINE
CORO_BEGIN_CONTEXT; CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic; INT_CONTEXT *pic;
bool bConverse;
CORO_END_CONTEXT(_ctx); CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx); CORO_BEGIN_CODE(_ctx);
// get the stuff copied to process when it was created // get the stuff copied to process when it was created
_ctx->pic = *((INT_CONTEXT **)param); _ctx->pic = *((INT_CONTEXT * const *)param);
_ctx->pic = RestoreInterpretContext(_ctx->pic); _ctx->pic = RestoreInterpretContext(_ctx->pic);
_ctx->bConverse = TinselV2 && (_ctx->pic->event == CONVERSE);
CORO_INVOKE_1(Interpret, _ctx->pic); CORO_INVOKE_1(Interpret, _ctx->pic);
// Restore control after CallScene() from a conversation icon
if (_ctx->bConverse)
ControlOn();
CORO_END_CODE; CORO_END_CODE;
} }
@ -500,10 +651,17 @@ static int CountOut = 1; // == 1 for immediate start of first scene
* When the count expires, the screen will have faded. Ensure the scene | * When the count expires, the screen will have faded. Ensure the scene |
* is loaded, clear the screen, and start the new scene. * is loaded, clear the screen, and start the new scene.
*/ */
void ChangeScene() { bool ChangeScene(bool bReset) {
// Prevent attempt to fade-out when restarting game
if (bReset) {
CountOut = 1; // immediate start of first scene again
DelayedScene.scene = HookScene.scene = 0;
return false;
}
if (IsRestoringScene()) if (IsRestoringScene())
return; return true;
if (NextScene.scene != 0) { if (NextScene.scene != 0) {
if (!CountOut) { if (!CountOut) {
@ -517,12 +675,15 @@ void ChangeScene() {
// Trigger pre-load and fade and start countdown // Trigger pre-load and fade and start countdown
CountOut = COUNTOUT_COUNT; CountOut = COUNTOUT_COUNT;
FadeOutFast(NULL); FadeOutFast(NULL);
if (TinselV2)
_vm->_pcmMusic->startFadeOut(COUNTOUT_COUNT);
break; break;
} }
} else if (--CountOut == 0) { } else if (--CountOut == 0) {
if (!TinselV2)
ClearScreen(); ClearScreen();
NewScene(NextScene.scene, NextScene.entry); StartNewScene(NextScene.scene, NextScene.entry);
NextScene.scene = 0; NextScene.scene = 0;
switch (NextScene.trans) { switch (NextScene.trans) {
@ -535,20 +696,32 @@ void ChangeScene() {
SetDoFadeIn(true); SetDoFadeIn(true);
break; break;
} }
} else
_vm->_pcmMusic->fadeOutIteration();
} }
}
return false;
}
/**
* CuttingScene
*/
void CuttingScene(bool bCutting) {
bCuttingScene = bCutting;
if (!bCutting)
WrapScene();
} }
/** /**
* LoadBasicChunks * LoadBasicChunks
*/ */
void LoadBasicChunks(void) { void LoadBasicChunks(void) {
byte *cptr; byte *cptr;
int numObjects; int numObjects;
// Allocate RAM for savescene data // Allocate RAM for savescene data
InitialiseSs(); InitialiseSaveScenes();
// CHUNK_TOTAL_ACTORS seems to be missing in the released version, hard coding a value // CHUNK_TOTAL_ACTORS seems to be missing in the released version, hard coding a value
// TODO: Would be nice to just change 511 to MAX_SAVED_ALIVES // TODO: Would be nice to just change 511 to MAX_SAVED_ALIVES
@ -581,6 +754,23 @@ void LoadBasicChunks(void) {
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY); cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY);
if (cptr != NULL) if (cptr != NULL)
MaxPolygons(*cptr); MaxPolygons(*cptr);
if (TinselV2) {
// Global processes
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_NUM_PROCESSES);
assert(cptr && (*cptr < 100));
int num = *cptr;
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_PROCESSES);
assert(!num || cptr);
GlobalProcesses(num, cptr);
// CdPlay() stuff
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_CDPLAY_HANDLE);
assert(cptr);
uint32 playHandle = READ_LE_UINT32(cptr);
assert(playHandle < 512);
SetCdPlayHandle(playHandle);
}
} }
//----------------- TinselEngine -------------------- //----------------- TinselEngine --------------------
@ -602,10 +792,39 @@ static const GameSettings tinselSettings[] = {
{NULL, NULL, 0, 0, NULL} {NULL, NULL, 0, 0, NULL}
}; };
const char *TinselEngine::_sampleIndices[][3] = {
{ "english.idx", "english1.idx", "english2.idx" },
{ "french.idx", "french1.idx", "french2.idx" },
{ "german.idx", "german1.idx", "german2.idx" },
{ "italian.idx", "italian1.idx", "italian2.idx" },
{ "spanish.idx", "spanish1.idx", "spanish2.idx" },
};
const char *TinselEngine::_sampleFiles[][3] = {
{ "english.smp", "english1.smp", "english2.smp" },
{ "french.smp", "french1.smp", "french2.smp" },
{ "german.smp", "german1.smp", "german2.smp" },
{ "italian.smp", "italian1.smp", "italian2.smp" },
{ "spanish.smp", "spanish1.smp", "spanish2.smp" },
};
const char *TinselEngine::_textFiles[][3] = {
{ "english.txt", "english1.txt", "english2.txt" },
{ "french.txt", "french1.txt", "french2.txt" },
{ "german.txt", "german1.txt", "german2.txt" },
{ "italian.txt", "italian1.txt", "italian2.txt" },
{ "spanish.txt", "spanish1.txt", "spanish2.txt" }
};
TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) : TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc) { Engine(syst), _gameDescription(gameDesc) {
_vm = this; _vm = this;
// Register debug flags
Common::addSpecialDebugLevel(kTinselDebugAnimations, "animations", "Animations debugging");
Common::addSpecialDebugLevel(kTinselDebugActions, "actions", "Actions debugging");
Common::addSpecialDebugLevel(kTinselDebugSound, "sound", "Sound debugging");
Common::addSpecialDebugLevel(kTinselDebugMusic, "music", "Music debugging");
// Setup mixer // Setup mixer
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
@ -629,9 +848,10 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (native_mt32) if (native_mt32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
_music = new MusicPlayer(_driver); _midiMusic = new MidiMusicPlayer(_driver);
//_music->setNativeMT32(native_mt32); _pcmMusic = new PCMMusicPlayer();
//_music->setAdlib(adlib); //_midiMusic->setNativeMT32(native_mt32);
//_midiMusic->setAdlib(adlib);
_musicVolume = ConfMan.getInt("music_volume"); _musicVolume = ConfMan.getInt("music_volume");
@ -644,25 +864,38 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
} }
TinselEngine::~TinselEngine() { TinselEngine::~TinselEngine() {
if (MoviePlaying())
FinishBMV();
delete _sound; delete _sound;
delete _music; delete _midiMusic;
delete _pcmMusic;
delete _console; delete _console;
delete _driver; delete _driver;
_screenSurface.free(); _screenSurface.free();
FreeSs(); FreeSaveScenes();
FreeTextBuffer(); FreeTextBuffer();
FreeHandleTable(); FreeHandleTable();
FreeActors(); FreeActors();
FreeObjectList(); FreeObjectList();
FreeGlobalProcesses();
FreeGlobals(); FreeGlobals();
delete _scheduler; delete _scheduler;
} }
Common::Error TinselEngine::init() { Common::Error TinselEngine::init() {
// Initialize backend // Initialize backend
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false); if (getGameID() == GID_DW2) {
#ifndef DW2_EXACT_SIZE
_screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, 1); initGraphics(640, 480, true);
#else
initGraphics(640, 432, true);
#endif
_screenSurface.create(640, 432, 1);
} else {
initGraphics(320, 200, false);
_screenSurface.create(320, 200, 1);
}
g_system->getEventManager()->registerRandomSource(_random, "tinsel"); g_system->getEventManager()->registerRandomSource(_random, "tinsel");
@ -670,6 +903,8 @@ Common::Error TinselEngine::init() {
_scheduler = new Scheduler(); _scheduler = new Scheduler();
InitSysVars();
// init memory manager // init memory manager
MemoryInit(); MemoryInit();
@ -684,20 +919,19 @@ Common::Error TinselEngine::init() {
RebootCursor(); RebootCursor();
RebootDeadTags(); RebootDeadTags();
RebootMovers(); RebootMovers();
resetUserEventTime();
RebootTimers(); RebootTimers();
RebootScalingReels(); RebootScalingReels();
DelayedScene.scene = HookScene.scene = 0; DelayedScene.scene = HookScene.scene = 0;
#endif #endif
// Load in text strings
ChangeLanguage(g_language);
// Init palette and object managers, scheduler, keyboard and mouse // Init palette and object managers, scheduler, keyboard and mouse
RestartDrivers(); RestartDrivers();
// TODO: More stuff from dos_main.c may have to be added here
// load in text strings
ChangeLanguage(g_language);
// load in graphics info // load in graphics info
SetupHandleTable(); SetupHandleTable();
@ -707,14 +941,23 @@ Common::Error TinselEngine::init() {
return Common::kNoError; return Common::kNoError;
} }
void TinselEngine::syncSoundSettings() {
// Sync the engine with the config manager
int soundVolumeMusic = ConfMan.getInt("music_volume");
int soundVolumeSFX = ConfMan.getInt("sfx_volume");
int soundVolumeSpeech = ConfMan.getInt("speech_volume");
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
}
Common::String TinselEngine::getSavegameFilename(int16 saveNum) const { Common::String TinselEngine::getSavegameFilename(int16 saveNum) const {
char filename[256]; char filename[256];
snprintf(filename, 256, "%s.%03d", getTargetName().c_str(), saveNum); snprintf(filename, 256, "%s.%03d", getTargetName().c_str(), saveNum);
return filename; return filename;
} }
#define GAME_FRAME_DELAY (1000 / ONE_SECOND)
Common::Error TinselEngine::go() { Common::Error TinselEngine::go() {
uint32 timerVal = 0; uint32 timerVal = 0;
@ -758,6 +1001,9 @@ Common::Error TinselEngine::go() {
// Save/Restore scene file transfers // Save/Restore scene file transfers
ProcessSRQueue(); ProcessSRQueue();
// Handle any playing movie
FettleBMV();
#ifdef DEBUG #ifdef DEBUG
if (bFast) if (bFast)
continue; // run flat-out continue; // run flat-out
@ -765,6 +1011,11 @@ Common::Error TinselEngine::go() {
// Loop processing events while there are any pending // Loop processing events while there are any pending
while (pollEvent()); while (pollEvent());
DoCdChange();
if (MoviePlaying() && NextMovieTime())
g_system->delayMillis(MAX<int>(NextMovieTime() - g_system->getMillis() + MovieAudioLag(), 0));
else
g_system->delayMillis(10); g_system->delayMillis(10);
} }
@ -776,8 +1027,11 @@ Common::Error TinselEngine::go() {
void TinselEngine::NextGameCycle(void) { void TinselEngine::NextGameCycle(void) {
// // Dim Music
ChangeScene(); _pcmMusic->dimIteration();
// Check for scene change
ChangeScene(false);
// Allow a user event for this schedule // Allow a user event for this schedule
ResetEcount(); ResetEcount();
@ -785,6 +1039,9 @@ void TinselEngine::NextGameCycle(void) {
// schedule process // schedule process
_scheduler->schedule(); _scheduler->schedule();
if (MoviePlaying())
CopyMovieToScreen();
else
// redraw background // redraw background
DrawBackgnd(); DrawBackgnd();
@ -810,7 +1067,13 @@ bool TinselEngine::pollEvent() {
break; break;
case Common::EVENT_MOUSEMOVE: case Common::EVENT_MOUSEMOVE:
_mousePos = event.mouse; {
// This fragment takes care of Tinsel 2 when it's been compiled with
// blank areas at the top and bottom of thes creen
int ySize = (g_system->getHeight() - _vm->screen().h) / 2;
if ((event.mouse.y >= ySize) && (event.mouse.y < (g_system->getHeight() - ySize)))
_mousePos = Common::Point(event.mouse.x, event.mouse.y - ySize);
}
break; break;
case Common::EVENT_KEYDOWN: case Common::EVENT_KEYDOWN:
@ -828,7 +1091,6 @@ bool TinselEngine::pollEvent() {
/** /**
* Start the processes that continue between scenes. * Start the processes that continue between scenes.
*/ */
void TinselEngine::CreateConstProcesses(void) { void TinselEngine::CreateConstProcesses(void) {
// Process to run the master script // Process to run the master script
_scheduler->createProcess(PID_MASTER_SCR, MasterScriptProcess, NULL, 0); _scheduler->createProcess(PID_MASTER_SCR, MasterScriptProcess, NULL, 0);
@ -841,7 +1103,6 @@ void TinselEngine::CreateConstProcesses(void) {
/** /**
* Restart the game * Restart the game
*/ */
void TinselEngine::RestartGame(void) { void TinselEngine::RestartGame(void) {
HoldItem(INV_NOICON); // Holding nothing HoldItem(INV_NOICON); // Holding nothing
@ -878,7 +1139,6 @@ void TinselEngine::RestartGame(void) {
/** /**
* Init palette and object managers, scheduler, keyboard and mouse. * Init palette and object managers, scheduler, keyboard and mouse.
*/ */
void TinselEngine::RestartDrivers(void) { void TinselEngine::RestartDrivers(void) {
// init the palette manager // init the palette manager
ResetPalAllocator(); ResetPalAllocator();
@ -902,13 +1162,12 @@ void TinselEngine::RestartDrivers(void) {
} }
// Set midi volume // Set midi volume
SetMidiVolume(volMidi); SetMidiVolume(volMusic);
} }
/** /**
* Remove keyboard, mouse and joystick drivers. * Remove keyboard, mouse and joystick drivers.
*/ */
void TinselEngine::ChopDrivers(void) { void TinselEngine::ChopDrivers(void) {
// remove sound driver // remove sound driver
StopMidi(); StopMidi();
@ -923,7 +1182,6 @@ void TinselEngine::ChopDrivers(void) {
/** /**
* Process a keyboard event * Process a keyboard event
*/ */
void TinselEngine::ProcessKeyEvent(const Common::Event &event) { void TinselEngine::ProcessKeyEvent(const Common::Event &event) {
// Handle any special keys immediately // Handle any special keys immediately
@ -974,4 +1232,48 @@ void TinselEngine::ProcessKeyEvent(const Common::Event &event) {
keypresses.push_back(event); keypresses.push_back(event);
} }
const char *TinselEngine::getSampleIndex(LANGUAGE lang) {
int cd;
if (TinselV2) {
cd = GetCurrentCD();
assert((cd == 1) || (cd == 2));
assert(((unsigned int) lang) < 5);
} else {
cd = 0;
lang = TXT_ENGLISH;
}
return _sampleIndices[lang][cd];
}
const char *TinselEngine::getSampleFile(LANGUAGE lang) {
int cd;
if (TinselV2) {
cd = GetCurrentCD();
assert((cd == 1) || (cd == 2));
assert(((unsigned int) lang) < 5);
} else {
cd = 0;
lang = TXT_ENGLISH;
}
return _sampleFiles[lang][cd];
}
const char *TinselEngine::getTextFile(LANGUAGE lang) {
assert(((unsigned int) lang) < 5);
int cd;
if (TinselV2) {
cd = GetCurrentCD();
assert((cd == 1) || (cd == 2));
} else
cd = 0;
return _textFiles[lang][cd];
}
} // End of namespace Tinsel } // End of namespace Tinsel

View file

@ -28,6 +28,7 @@
#include "common/scummsys.h" #include "common/scummsys.h"
#include "common/system.h" #include "common/system.h"
#include "common/error.h"
#include "common/events.h" #include "common/events.h"
#include "common/keyboard.h" #include "common/keyboard.h"
#include "common/util.h" #include "common/util.h"
@ -39,10 +40,12 @@
#include "tinsel/debugger.h" #include "tinsel/debugger.h"
#include "tinsel/graphics.h" #include "tinsel/graphics.h"
#include "tinsel/sound.h" #include "tinsel/sound.h"
#include "tinsel/dw.h"
namespace Tinsel { namespace Tinsel {
class MusicPlayer; class MidiMusicPlayer;
class PCMMusicPlayer;
class Scheduler; class Scheduler;
class SoundManager; class SoundManager;
@ -65,11 +68,34 @@ enum TinselGameFeatures {
GF_USE_5FLAGS = 1 << 6 // All 5 flags GF_USE_5FLAGS = 1 << 6 // All 5 flags
}; };
/**
* The following is the ScummVM definitions of the various Tinsel versions:
* TINSEL_V0 - This was an early engine version that was only used in the Discworld 1
* demo. It is not currently supported.
* TINSEL_V1 - This was the engine version used by Discworld 1. Note that there were two
* major releases: an earlier version that used *.gra files, and a later one that
* used *.scn files, and contained certain script and engine bugfixes. In ScummVM,
* we treat both releases as 'Tinsel 1', since the engine fixes from the later
* version work equally well the earlier version data.
* TINSEL_V2 - This is the engine used for the Discworld 2 game.
*/
enum TinselEngineVersion { enum TinselEngineVersion {
TINSEL_V0 = 0, // Used in the DW1 demo only TINSEL_V0 = 0,
TINSEL_V1 = 1 TINSEL_V1 = 1,
TINSEL_V2 = 2
}; };
enum {
kTinselDebugAnimations = 1 << 0,
kTinselDebugActions = 1 << 1,
kTinselDebugSound = 1 << 2,
kTinselDebugMusic = 2 << 3
};
#define DEBUG_BASIC 1
#define DEBUG_INTERMEDIATE 2
#define DEBUG_DETAILED 3
struct TinselGameDescription; struct TinselGameDescription;
enum TinselKeyDirection { enum TinselKeyDirection {
@ -79,6 +105,22 @@ enum TinselKeyDirection {
typedef bool (*KEYFPTR)(const Common::KeyState &); typedef bool (*KEYFPTR)(const Common::KeyState &);
#define SCREEN_WIDTH (_vm->screen().w) // PC screen dimensions
#define SCREEN_HEIGHT (_vm->screen().h)
#define SCRN_CENTRE_X ((SCREEN_WIDTH - 1) / 2) // screen centre x
#define SCRN_CENTRE_Y ((SCREEN_HEIGHT - 1) / 2) // screen centre y
#define UNUSED_LINES 48
#define EXTRA_UNUSED_LINES 3
//#define SCREEN_BOX_HEIGHT1 (SCREEN_HEIGHT - UNUSED_LINES)
//#define SCREEN_BOX_HEIGHT2 (SCREEN_BOX_HEIGHT1 - EXTRA_UNUSED_LINES)
#define SCREEN_BOX_HEIGHT1 SCREEN_HEIGHT
#define SCREEN_BOX_HEIGHT2 SCREEN_HEIGHT
#define GAME_FRAME_DELAY (1000 / ONE_SECOND)
#define TinselVersion (_vm->getVersion())
#define TinselV2 (TinselVersion == TINSEL_V2)
class TinselEngine : public Engine { class TinselEngine : public Engine {
int _gameId; int _gameId;
Common::KeyState _keyPressed; Common::KeyState _keyPressed;
@ -89,11 +131,16 @@ class TinselEngine : public Engine {
Console *_console; Console *_console;
Scheduler *_scheduler; Scheduler *_scheduler;
static const char *_sampleIndices[][3];
static const char *_sampleFiles[][3];
static const char *_textFiles[][3];
protected: protected:
// Engine APIs // Engine APIs
virtual Common::Error init(); virtual Common::Error init();
virtual Common::Error go(); virtual Common::Error go();
virtual void syncSoundSettings();
public: public:
TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc); TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc);
@ -109,13 +156,18 @@ public:
uint16 getVersion() const; uint16 getVersion() const;
Common::Platform getPlatform() const; Common::Platform getPlatform() const;
const char *getSampleIndex(LANGUAGE lang);
const char *getSampleFile(LANGUAGE lang);
const char *getTextFile(LANGUAGE lang);
MidiDriver *_driver; MidiDriver *_driver;
SoundManager *_sound; SoundManager *_sound;
MusicPlayer *_music; MidiMusicPlayer *_midiMusic;
PCMMusicPlayer *_pcmMusic;
KEYFPTR _keyHandler; KEYFPTR _keyHandler;
private: private:
//MusicPlayer *_music; //MidiMusicPlayer *_midiMusic;
int _musicVolume; int _musicVolume;
void NextGameCycle(void); void NextGameCycle(void);
@ -134,7 +186,8 @@ public:
Common::Point getMousePosition() const { return _mousePos; } Common::Point getMousePosition() const { return _mousePos; }
void setMousePosition(const Common::Point &pt) { void setMousePosition(const Common::Point &pt) {
g_system->warpMouse(pt.x, pt.y); int ySize = (g_system->getHeight() - _screenSurface.h) / 2;
g_system->warpMouse(pt.x, pt.y + ySize);
_mousePos = pt; _mousePos = pt;
} }
void divertKeyInput(KEYFPTR fptr) { _keyHandler = fptr; } void divertKeyInput(KEYFPTR fptr) { _keyHandler = fptr; }
@ -145,6 +198,11 @@ public:
// Global reference to the TinselEngine object // Global reference to the TinselEngine object
extern TinselEngine *_vm; extern TinselEngine *_vm;
// Externally available methods
void CuttingScene(bool bCutting);
void CDChangeForRestore(int cdNumber);
void CdHasChanged(void);
} // End of namespace Tinsel } // End of namespace Tinsel
#endif /* TINSEL_H */ #endif /* TINSEL_H */