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/events.h" // for USER_EVENT
#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/palette.h" // for COLORREF
namespace Tinsel {
struct FREEL;
struct INT_CONTEXT;
struct MACTOR;
struct MOVER;
struct OBJECT;
#define ACTORTAG_KEY 0x1000000
#define OTH_RELATEDACTOR 0x00000fff
#define OTH_RELATIVE 0x00001000
#define OTH_ABSOLUTE 0x00002000
/*----------------------------------------------------------------------*/
void RegisterActors(int num);
void FreeActors(void);
void setleadid(int rid);
int LeadId(void);
void StartActors(SCNHANDLE ah, int numActors, bool bRunScript);
void SetLeadId(int rid);
int GetLeadId(void);
bool ActorIsGhost(int actor);
void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
void DropActors(void); // No actor reels running
void DisableActor(int actor);
void EnableActor(int actor);
@ -63,60 +69,110 @@ int GetActorLeft(int ano);
int GetActorRight(int ano);
int GetActorTop(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);
void unHideMovingActor(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);
SCNHANDLE actorFilm(int ano);
void setActorPlayFilm(int ano, SCNHANDLE film);
SCNHANDLE getActorPlayFilm(int ano);
void setActorTalkFilm(int ano, SCNHANDLE film);
SCNHANDLE getActorTalkFilm(int ano);
void setActorTalking(int ano, bool tf);
bool isActorTalking(int ano);
void setActorLatestFilm(int ano, SCNHANDLE film);
SCNHANDLE getActorLatestFilm(int ano);
void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorPlayFilm(int ano);
void SetActorTalkFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorTalkFilm(int ano);
void SetActorTalking(int ano, bool tf);
bool ActorIsTalking(int ano);
void SetActorLatestFilm(int ano, SCNHANDLE hFilm);
SCNHANDLE GetActorLatestFilm(int ano);
void updateActorEsc(int ano, bool escOn, int escEv);
bool actorEsc(int ano);
int actorEev(int ano);
void storeActorPos(int ano, int x, int y);
void storeActorSteps(int ano, int steps);
int getActorSteps(int ano);
void storeActorZpos(int ano, int z);
void UpdateActorEsc(int ano, bool escOn, int escEvent);
void UpdateActorEsc(int ano, int escEvent);
bool ActorEsc(int ano);
int ActorEev(int ano);
void StoreActorPos(int ano, int x, int y);
void StoreActorSteps(int ano, int steps);
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);
void FirstTaggedActor(void);
int NextTaggedActor(void);
int NextTaggedActor(int previous);
int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
void MAsetZPos(MACTOR *pActor, int y, int32 zFactor);
void actorEvent(int ano, USER_EVENT event, BUTEVENT be);
void SetMoverZ(MOVER *pMover, int y, int32 zFactor);
void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
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 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 {
short actorID;
short z;
short zFactor;
bool bAlive;
bool bHidden;
SCNHANDLE presFilm; //!< the film that reel belongs to
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);
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/object.h"
#include "tinsel/sched.h"
#include "tinsel/tinsel.h"
#include "common/util.h"
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.
* @param pAnim Animation data structure
@ -64,6 +44,9 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
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)) {
case ANI_END: // end of animation script
@ -104,7 +87,6 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
// go fetch a real image
break;
case ANI_HVFLIP: // flip animated object in both directions
// next opcode
@ -230,6 +212,12 @@ SCRIPTSTATE DoNextFrame(ANIM *pAnim) {
void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
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->pObject = pAniObj; // set object to animate
pAnim->hScript = hNewScript; // set animation script
@ -254,9 +242,13 @@ SCRIPTSTATE StepAnimScript(ANIM *pAnim) {
// re-init animation delta counter
pAnim->aniDelta = pAnim->aniRate;
if (TinselV2)
state = DoNextFrame(pAnim);
else {
// move to next frame
while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
;
}
return state;
}
@ -274,7 +266,7 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// get a pointer to the script
const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
if (numFrames <= 0)
if (!TinselV2 && (numFrames <= 0))
// do nothing
return;
@ -282,9 +274,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
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)!'");
break;
return;
case ANI_JUMP: // do animation jump
@ -293,6 +286,10 @@ void SkipFrames(ANIM *pAnim, int numFrames) {
// jump to new frame position
pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op);
if (TinselV2)
// Done if skip to jump
return;
break;
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
// one less frame
if (numFrames-- > 0) {
if (numFrames == 0)
return;
if (numFrames == -1 || numFrames-- > 0) {
// next opcode
pAnim->scriptIndex++;
} 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

View file

@ -41,6 +41,32 @@ struct ANIM {
uint32 hScript; //!< animation script handle
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
int numFrames); // number of frames to skip
bool AboutToJumpOrEnd(PANIM pAnim);
} // end of namespace Tinsel
#endif // TINSEL_ANIM_H

View file

@ -37,6 +37,9 @@ namespace Tinsel {
// current background
BACKGND *pCurBgnd = NULL;
// FIXME: Not yet used
static bool bEntireRedraw;
/**
* Called to initialise a 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);
}
/**
* 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.
* @param which Which playfield
@ -229,4 +253,9 @@ void DrawBackgnd(void) {
ResetClipRect();
}
void ForceEntireRedraw(void) {
bEntireRedraw = true;
}
} // end of namespace Tinsel

View file

@ -27,10 +27,11 @@
#ifndef TINSEL_BACKGND_H // prevent multiple includes
#define TINSEL_BACKGND_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/palette.h" // palette definitions
#include "common/frac.h"
#include "common/rect.h"
#include "tinsel/coroutine.h"
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/palette.h" // palette definitions
namespace Tinsel {
@ -78,6 +79,8 @@ struct BACKGND {
void InitBackground( // called to initialise a 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 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 *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
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
SCNHANDLE BackPal(void);
OBJECT *GetBgObject();
SCNHANDLE BgPal(void);
void ForceEntireRedraw(void);
int BgWidth(void);
int BgHeight(void);
} // end of namespace Tinsel

View file

@ -37,7 +37,8 @@
#include "tinsel/pid.h"
#include "tinsel/sched.h"
#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"
@ -45,49 +46,61 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
static SCNHANDLE BackPalette = 0; // Background's palette
static OBJECT *pBG = 0; // The main picture's object.
#define MAX_BG 10
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 SCNHANDLE BgroundHandle = 0; // Current scene handle - stored in case of Save_Scene()
static bool DoFadeIn = false;
static ANIM thisAnim; // used by BGmainProcess()
static SCNHANDLE hBackground = 0; // Current scene handle - stored in case of Save_Scene()
static bool bDoFadeIn = false;
static int bgReels;
/**
* GetBgObject
*/
OBJECT *GetBgObject() {
return pBG[0];
}
/**
* BackPal
*/
SCNHANDLE BackPal(void) {
return BackPalette;
SCNHANDLE BgPal(void) {
return hBgPal;
}
/**
* SetDoFadeIn
*/
void SetDoFadeIn(bool tf) {
DoFadeIn = tf;
bDoFadeIn = tf;
}
/**
* Called before scene change.
*/
void DropBackground(void) {
pBG = NULL; // No background
BackPalette = 0; // No background palette
pBG[0] = NULL; // No background
if (!TinselV2)
hBgPal = 0; // No background palette
}
/**
* Return the width of the current background.
*/
int BackgroundWidth(void) {
assert(pBG);
return MultiRightmost(pBG) + 1;
int BgWidth(void) {
assert(pBG[0]);
return MultiRightmost(pBG[0]) + 1;
}
/**
* Return the height of the current background.
*/
int BackgroundHeight(void) {
assert(pBG);
return MultiLowest(pBG) + 1;
int BgHeight(void) {
assert(pBG[0]);
return MultiLowest(pBG[0]) + 1;
}
/**
@ -100,90 +113,137 @@ static void BGmainProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
const FREEL *pfr;
const FILM *pFilm;
const FREEL *pReel;
const MULTI_INIT *pmi;
// get the stuff copied to process when it was created
pfr = (const FREEL *)param;
if (pBG == NULL) {
if (pBG[0] == NULL) {
/*** At start of scene ***/
if (!TinselV2) {
pReel = (const FREEL *)param;
// 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.
pBG = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG);
InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
pBG[0] = MultiInitObject(pmi);
MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[0]);
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) {
FadeInFast(NULL);
DoFadeIn = false;
int i;
for (i = 0; i < bgReels; i++) {
// 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)
CORO_SLEEP(1);
if (bDoFadeIn) {
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!");
}
CORO_SLEEP(1);
}
} else {
// 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.
InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
StepAnimScript(&thisAnim);
// Just re-initialise the scripts.
for (int i = 0; i < bgReels; i++) {
InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed);
StepAnimScript(&thisAnim[i]);
}
}
}
CORO_END_CODE;
}
/**
* setBackPal()
* AetBgPal()
*/
void setBackPal(SCNHANDLE hPal) {
BackPalette = hPal;
void SetBackPal(SCNHANDLE hPal) {
hBgPal = hPal;
fettleFontPal(BackPalette);
CreateTranslucentPalette(BackPalette);
FettleFontPal(hBgPal);
CreateTranslucentPalette(hBgPal);
}
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
* everything else's use, then starts a display process for each reel
* 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;
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);
setBackPal(FROM_LE_32(pim->hImgPal));
pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm);
SetBackPal(FROM_LE_32(pim->hImgPal));
// Extract the film speed
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
assert(FROM_LE_32(pfilm->numreels) == 1); // Multi-reeled backgrounds withdrawn
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.
*/
SCNHANDLE GetBgroundHandle(void) {
return BgroundHandle;
return hBackground;
}
} // 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
*/
//#define USE_3FLAGS 1
#include "tinsel/config.h"
#include "tinsel/dw.h"
#include "tinsel/sound.h"
@ -39,7 +41,7 @@ namespace Tinsel {
//----------------- GLOBAL GLOBAL DATA --------------------
int dclickSpeed = DOUBLE_CLICK_TIME;
int volMidi = Audio::Mixer::kMaxChannelVolume;
int volMusic = Audio::Mixer::kMaxChannelVolume;
int volSound = Audio::Mixer::kMaxChannelVolume;
int volVoice = Audio::Mixer::kMaxChannelVolume;
int speedText = DEFTEXTSPEED;
@ -49,21 +51,18 @@ LANGUAGE g_language = TXT_ENGLISH;
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.
*/
void WriteConfig(void) {
ConfMan.setInt("dclick_speed", dclickSpeed);
ConfMan.setInt("music_volume", volMidi);
ConfMan.setInt("music_volume", volMusic);
ConfMan.setInt("sfx_volume", volSound);
ConfMan.setInt("speech_volume", volVoice);
ConfMan.setInt("talkspeed", (speedText * 255) / 100);
ConfMan.setBool("subtitles", bSubtitles);
//ConfMan.setBool("swap_buttons", bSwapButtons ? 1 : 0);
//ConfigData.bAmerica = bAmerica; // EN_USA / EN_GRB
// Store language for multilingual versions
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();
}
/*---------------------------------------------------------------------*\
| ReadConfig() |
|-----------------------------------------------------------------------|
|
\*---------------------------------------------------------------------*/
/**
* Read configuration settings from the config file into memory
*/
void ReadConfig(void) {
if (ConfMan.hasKey("dclick_speed"))
dclickSpeed = ConfMan.getInt("dclick_speed");
volMidi = ConfMan.getInt("music_volume");
volMusic = ConfMan.getInt("music_volume");
volSound = ConfMan.getInt("sfx_volume");
volVoice = ConfMan.getInt("speech_volume");

View file

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

View file

@ -70,6 +70,7 @@ struct CoroBaseContext {
typedef CoroBaseContext *CoroContext;
extern CoroContext nullContext;
/**
* 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_BEGIN_CODE(x) \
if (&coroParam == &nullContext) assert(!nullContext);\
if (!x) {coroParam = x = new CoroContextTag();}\
assert(coroParam);\
assert(coroParam->_sleep >= 0);\
@ -109,17 +111,22 @@ public:
switch(coroParam->_line) { case 0:;
#define CORO_END_CODE \
if (&coroParam == &nullContext) nullContext = NULL; \
}
#define CORO_SLEEP(delay) \
do {\
#define CORO_SLEEP(delay) do {\
coroParam->_line = __LINE__;\
coroParam->_sleep = delay;\
assert(&coroParam != &nullContext);\
return; case __LINE__:;\
} 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 */
#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 */
#define CORO_INVOKE_ARGS(subCoro, ARGS) \
@ -130,9 +137,22 @@ public:
subCoro ARGS;\
if (!coroParam->_subctx) break;\
coroParam->_sleep = coroParam->_subctx->_sleep;\
assert(&coroParam != &nullContext);\
return; case __LINE__:;\
} while(1);\
} 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) \
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX))
@ -140,6 +160,8 @@ public:
CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0))
#define CORO_INVOKE_2(subCoroutine, 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

View file

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

View file

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

View file

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

View file

@ -31,7 +31,6 @@
#include "tinsel/tinsel.h"
#include "tinsel/savescn.h" // needed by TinselMetaEngine::listSaves
namespace Tinsel {
struct TinselGameDescription {
@ -68,7 +67,7 @@ uint16 TinselEngine::getVersion() const {
static const PlainGameDescriptor tinselGames[] = {
{"tinsel", "Tinsel engine game"},
{"dw", "Discworld"},
{"dw2", "Discworld 2: Mortality Bytes!"},
{"dw2", "Discworld 2: Missing Presumed ...!?"},
{0, 0}
};
@ -77,25 +76,15 @@ namespace Tinsel {
static const TinselGameDescription gameDescriptions[] = {
// The DW1 demo was based on an older revision of the Tinsel engine
// than the one used in the released game. We call it Tinsel v0 as
// opposed to v1 which was used in the full retail version of DW.
{ // 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,
},
// Note: The following is the (hopefully) definitive list of version details:
// TINSEL_V0: Used only by the Discworld 1 demo - this used a more primitive version
// 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
// 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,
// as both game versions work equally well with the newer code.
// TINSEL_V2: The Discworld 2 game used this updated version of the Tinsel 1 engine,
// and as far as we know there aren't any variations of this engine.
{
{ // This version has *.gra files
@ -112,49 +101,6 @@ static const TinselGameDescription gameDescriptions[] = {
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.
// Note: It contains no english subtitles.
{
@ -245,7 +191,7 @@ static const TinselGameDescription gameDescriptions[] = {
TINSEL_V1,
},
{ // English CD with SCN files
{ // English CD v2
{
"dw",
"CD",
@ -265,7 +211,7 @@ static const TinselGameDescription gameDescriptions[] = {
},
#if 0
{ // English Saturn CD. Not (yet?) supported
{ // English Saturn CD
{
"dw",
"CD",
@ -285,6 +231,25 @@ static const TinselGameDescription gameDescriptions[] = {
},
#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"
// Note: This release has ENGLISH.TXT (with german content) instead of GERMAN.TXT
{
@ -301,6 +266,44 @@ static const TinselGameDescription gameDescriptions[] = {
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 }
};
@ -334,25 +337,23 @@ public:
}
virtual const char *getCopyright() const {
// FIXME: Bad copyright string.
// Should be something like "Tinsel (C) Psygnosis" or so... ???
return "Tinsel Engine";
return "Tinsel (C) Psygnosis";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
virtual SaveStateList listSaves(const char *target) const;
virtual int getMaximumSaveSlot() const;
};
bool TinselMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves);
return (f == kSupportsListSaves);
}
namespace Tinsel {
extern int getList(Common::SaveFileManager *saveFileMan, const Common::String &target);
}
SaveStateList TinselMetaEngine::listSaves(const char *target) const {
@ -368,8 +369,6 @@ SaveStateList TinselMetaEngine::listSaves(const char *target) const {
return saveList;
}
int TinselMetaEngine::getMaximumSaveSlot() const { return 999; }
bool TinselMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const {
const Tinsel::TinselGameDescription *gd = (const Tinsel::TinselGameDescription *)desc;
if (gd) {

File diff suppressed because it is too large Load diff

View file

@ -29,52 +29,67 @@
#define TINSEL_INVENTORY_H
#include "tinsel/dw.h"
#include "tinsel/events.h" // for KEYEVENT, BUTEVENT
#include "tinsel/events.h" // for PLR_EVENT, PLR_EVENT
namespace Tinsel {
class Serializer;
enum {
INV_OPEN = -1,
INV_OPEN = -1, // DW1 only
INV_CONV = 0,
INV_1 = 1,
INV_2 = 2,
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 */
struct INV_OBJECT {
int32 id; // inventory objects id
SCNHANDLE hFilm; // inventory objects animation film
SCNHANDLE hIconFilm; // inventory objects animation film
SCNHANDLE hScript; // inventory objects event handling script
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);
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 Ymovement(int y);
void ButtonToInventory(BUTEVENT be);
void KeyToInventory(KEYEVENT ke);
void EventToInventory(PLR_EVENT pEvent, const Common::Point &coOrds);
void ButtonToInventory(PLR_EVENT be);
void KeyToInventory(PLR_EVENT ke);
int WhichItemHeld(void);
void HoldItem(int item);
void HoldItem(int item, bool bKeepFilm = false);
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);
@ -89,25 +104,34 @@ void idec_inv2(SCNHANDLE text, int MaxContents, int MinWidth, int MinHeight,
bool InventoryActive(void);
void AddIconToPermanentDefaultList(int icon);
void PermaConvIcon(int icon, bool bEnd = false);
void convPos(int bpos);
void ConvPoly(HPOLYGON hp);
int convIcon(void);
int GetIcon(void);
void CloseDownConv(void);
void convHide(bool hide);
bool convHid(void);
void HideConversation(bool hide);
bool ConvIsHidden(void);
enum {
NOOBJECT = -1,
INV_NOICON = -1,
INV_CLOSEICON = -2,
INV_OPENICON = -3,
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 setFlagFilms(SCNHANDLE hf);
@ -122,8 +146,6 @@ bool IsInInventory(int object, int invnum);
void KillInventory(void);
void invObjectFilm(int object, SCNHANDLE hFilm);
void syncInvInfo(Serializer &s);
int InvGetLimit(int invno);
@ -134,9 +156,13 @@ void InvSetSize(int invno, int MinWidth, int MinHeight,
int WhichInventoryOpen(void);
bool IsTopWindow(void);
bool IsConfWindow(void);
bool MenuActive(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
#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 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 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
#define MIN_INT (1 << (8*sizeof(int) - 1))
@ -70,8 +69,7 @@ typedef int HPOLYGON;
#define FIELD_WORLD 0
#define FIELD_STATUS 1
#define ZSHIFT 10
// We don't set the Z position for print and talk text
// 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_SAVED_ACTORS 32 // Saved 'Normal' actors
#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()
#define NO_ENTRY_NUM (-3458) // Magic unlikely number
@ -110,13 +109,19 @@ typedef int HPOLYGON;
// Language for the resource strings
enum LANGUAGE {
TXT_ENGLISH,
TXT_FRENCH,
TXT_GERMAN,
TXT_ITALIAN,
TXT_SPANISH
TXT_ENGLISH, TXT_FRENCH, TXT_GERMAN, TXT_ITALIAN, TXT_SPANISH,
TXT_HEBREW, TXT_HUNGARIAN, TXT_JAPANESE, TXT_US,
NUM_LANGUAGES
};
#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
#endif // TINSEL_DW_H

View file

@ -41,13 +41,14 @@
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/sched.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
struct EP_INIT {
HPOLYGON hEpoly;
PMACTOR pActor;
PMOVER pMover;
int index;
};
@ -61,24 +62,32 @@ static void EffectProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CONTEXT;
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);
int x, y; // Lead actor position
// 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 {
CORO_SLEEP(1);
GetMActorPosition(to->pActor, &x, &y);
GetMoverPosition(to->pMover, &x, &y);
} while (InPolygon(x, y, EFFECT) == to->hEpoly);
// 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;
}
@ -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
* 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;
EP_INIT epi;
@ -97,10 +106,10 @@ static void FettleEffectPolys(int x, int y, int index, PMACTOR pActor) {
hPoly = InPolygon(x, y, EFFECT);
if (hPoly != NOPOLY) {
//Just entered effect polygon
SetMAinEffectPoly(index, true);
SetMoverInEffect(index, true);
epi.hEpoly = hPoly;
epi.pActor = pActor;
epi.pMover = pActor;
epi.index = index;
g_scheduler->createProcess(PID_TCODE, EffectProcess, &epi, sizeof(epi));
}
@ -118,10 +127,10 @@ void EffectPolyProcess(CORO_PARAM, const void *param) {
CORO_BEGIN_CODE(_ctx);
while (1) {
for (int i = 0; i < MAX_MOVERS; i++) {
PMACTOR pActor = GetLiveMover(i);
PMOVER pActor = GetLiveMover(i);
if (pActor != NULL) {
int x, y;
GetMActorPosition(pActor, &x, &y);
GetMoverPosition(pActor, &x, &y);
FettleEffectPolys(x, y, i, pActor);
}
}

View file

@ -26,14 +26,17 @@
*/
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/coroutine.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/handle.h" // For LockMem()
#include "tinsel/inventory.h"
#include "tinsel/dialogs.h"
#include "tinsel/move.h" // For walking lead actor
#include "tinsel/pcode.h" // For Interpret()
#include "tinsel/pdisplay.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h" // For walking lead actor
@ -41,6 +44,7 @@
#include "tinsel/scroll.h" // For DontScrollCursor()
#include "tinsel/timers.h" // DwGetCurrentTime()
#include "tinsel/tinlib.h" // For control()
#include "tinsel/tinsel.h"
#include "tinsel/token.h"
namespace Tinsel {
@ -51,22 +55,25 @@ namespace Tinsel {
extern int GetTaggedActor(void);
extern HPOLYGON GetTaggedPoly(void);
//----------------- EXTERNAL GLOBAL DATA ---------------------
extern bool bEnableF1;
extern bool bEnableMenu;
//----------------- LOCAL GLOBAL DATA --------------------
static int userEvents = 0; // Whenever a button or a key comes in
static uint32 lastUserEvent = 0; // Time it hapenned
static int butEvents = 0; // Single or double, left or right. Or escape key.
static int escEvents = 0; // Escape key
static int leftEvents = 0; // Single or double, left or right. Or 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 controlState;
static bool bStartOff;
static int controlX, controlY;
static bool bProvNotProcessed = false;
/**
* Gets called before each schedule, only 1 user action per schedule
* is allowed.
@ -87,12 +94,12 @@ void IncUserEvents(void) {
* If this is a double click, the process from the waiting single click
* gets killed.
*/
void AllowDclick(CORO_PARAM, BUTEVENT be) {
void AllowDclick(CORO_PARAM, PLR_EVENT be) {
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (be == BE_SLEFT) {
if (be == PLR_SLEFT) {
GetToken(TOKEN_LEFT_BUT);
CORO_SLEEP(dclickSpeed+1);
FreeToken(TOKEN_LEFT_BUT);
@ -103,94 +110,120 @@ void AllowDclick(CORO_PARAM, BUTEVENT be) {
break;
} else if (be == BE_DLEFT) {
} else if (be == PLR_DLEFT) {
GetToken(TOKEN_LEFT_BUT);
FreeToken(TOKEN_LEFT_BUT);
}
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.
* Return TRUE if control taken, FALSE if not.
*/
bool GetControl(int param) {
if (TestToken(TOKEN_CONTROL)) {
control(param);
if (TinselV2)
return GetControl();
else if (TestToken(TOKEN_CONTROL)) {
Control(param);
return true;
} else
return false;
}
struct TP_INIT {
HPOLYGON hPoly; // Polygon
USER_EVENT event; // Trigerring event
BUTEVENT bev; // To allow for double clicks
bool take_control; // Set if control should be taken
// while code is running.
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 GetControl(void) {
if (controlState == CONTROL_ON) {
ControlOff();
return true;
} else
return false;
}
/**
* Runs glitter code associated with a polygon.
*/
void RunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, BUTEVENT be, bool tc) {
TP_INIT to = { hPoly, event, be, tc, 0 };
bool ControlIsOn(void) {
if (TinselV2)
return (controlState == CONTROL_ON);
g_scheduler->createProcess(PID_TCODE, PolyTinselProcess, &to, sizeof(to));
}
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));
return TestToken(TOKEN_CONTROL);
}
//-----------------------------------------------------------------------
@ -206,22 +239,33 @@ struct WP_INIT {
static void WalkProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
PMACTOR pActor;
PMOVER pMover;
int thisWalk;
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);
_ctx->pActor = GetMover(LEAD_ACTOR);
if (_ctx->pActor->MActorState == NORM_MACTOR) {
assert(_ctx->pActor->hCpath != NOPOLY); // Lead actor is not in a path
_ctx->pMover = GetMover(LEAD_ACTOR);
GetToken(TOKEN_LEAD);
SetActorDest(_ctx->pActor, to->x, to->y, false, 0);
if (TinselV2 && MoverIs(_ctx->pMover) && !MoverIsSWalking(_ctx->pMover)) {
assert(_ctx->pMover->hCpath != NOPOLY); // Lead actor is not in a path
_ctx->thisWalk = SetActorDest(_ctx->pMover, to->x, to->y, false, 0);
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);
FreeToken(TOKEN_LEAD);
@ -230,7 +274,7 @@ static void WalkProcess(CORO_PARAM, const void *param) {
CORO_END_CODE;
}
void walkto(int x, int y) {
void WalkTo(int x, int y) {
WP_INIT to = { x, y };
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.
* 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 aniX, aniY;
HPOLYGON hPoly;
@ -249,19 +293,34 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
if (++eCount != 1)
return;
if ((actor = GetTaggedActor()) != 0)
actorEvent(actor, uEvent, be);
else if ((hPoly = GetTaggedPoly()) != NOPOLY)
if ((actor = GetTaggedActor()) != 0) {
// Event for a tagged actor
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);
else {
else if (uEvent != PROV_WALKTO)
PolygonEvent(nullContext, hPoly, uEvent, 0, false, 0);
} else {
GetCursorXY(&aniX, &aniY, true);
// There could be a poly involved which has no tag.
if ((hPoly = InPolygon(aniX, aniY, TAG)) != NOPOLY
|| (hPoly = InPolygon(aniX, aniY, EXIT)) != NOPOLY) {
if ((hPoly = InPolygon(aniX, aniY, TAG)) != 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);
} else if (uEvent == WALKTO)
walkto(aniX, aniY);
} else if ((uEvent == PROV_WALKTO) || (uEvent == WALKTO)) {
if (TinselV2)
ProcessedProvisional();
WalkTo(aniX, aniY);
}
}
}
@ -269,137 +328,155 @@ static void User_Event(USER_EVENT uEvent, BUTEVENT be) {
/**
* ProcessButEvent
*/
void ProcessButEvent(BUTEVENT be) {
IncUserEvents();
void ProcessButEvent(PLR_EVENT be) {
if (bSwapButtons) {
switch (be) {
case BE_SLEFT:
be = BE_SRIGHT;
case PLR_SLEFT:
be = PLR_SRIGHT;
break;
case BE_DLEFT:
be = BE_DRIGHT;
case PLR_DLEFT:
be = PLR_DRIGHT;
break;
case BE_SRIGHT:
be = BE_SLEFT;
case PLR_SRIGHT:
be = PLR_SLEFT;
break;
case BE_DRIGHT:
be = BE_DLEFT;
case PLR_DRIGHT:
be = PLR_DLEFT;
break;
case BE_LDSTART:
be = BE_RDSTART;
case PLR_DRAG1_START:
be = PLR_DRAG2_START;
break;
case BE_LDEND:
be = BE_RDEND;
case PLR_DRAG1_END:
be = PLR_DRAG2_END;
break;
case BE_RDSTART:
be = BE_LDSTART;
case PLR_DRAG2_START:
be = PLR_DRAG1_START;
break;
case BE_RDEND:
be = BE_LDEND;
case PLR_DRAG2_END:
be = PLR_DRAG1_END;
break;
default:
break;
}
}
// if (be == BE_SLEFT || be == BE_DLEFT || be == BE_SRIGHT || be == BE_DRIGHT)
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;
}
}
PlayerEvent(be, _vm->getMousePosition());
}
/**
* 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.
if (bEnableF1 && ke == OPTION_KEY)
control(CONTROL_ON);
if (bEnableMenu && pEvent == PLR_MENU)
Control(CONTROL_ON);
else
IncUserEvents();
if (ke == ESC_KEY) {
escEvents++;
butEvents++; // Yes, I do mean this
if (pEvent == PLR_ESCAPE) {
++escEvents;
++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
if (!TestToken(TOKEN_CONTROL) && (BUTEVENT)ke != BE_LDEND)
// Only allow events if player control is on
if (!ControlIsOn() && (pEvent != PLR_DRAG1_END))
return;
switch (ke) {
case QUIT_KEY:
PopUpConf(QUIT);
if (TinselV2 && InventoryActive()) {
int x, y;
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;
case OPTION_KEY:
PopUpConf(OPTION);
case PLR_MENU:
OpenMenu(MAIN_MENU);
break;
case SAVE_KEY:
PopUpConf(SAVE);
case PLR_JUMP:
OpenMenu(HOPPER_MENU1);
break;
case LOAD_KEY:
PopUpConf(LOAD);
case PLR_SAVE:
OpenMenu(SAVE_MENU);
break;
case WALKTO_KEY:
if (InventoryActive())
ButtonToInventory(BE_SLEFT);
case PLR_LOAD:
OpenMenu(LOAD_MENU);
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
User_Event(WALKTO, BE_NONE);
EventToInventory(PLR_SLEFT, coOrds);
break;
case ACTION_KEY:
if (InventoryActive())
ButtonToInventory(BE_DLEFT);
case PLR_ACTION:
REAL_ACTION_CHECK;
if (TinselV2 || !InventoryActive())
ProcessUserEvent(ACTION, coOrds, PLR_DLEFT);
else
User_Event(ACTION, BE_NONE);
EventToInventory(PLR_DLEFT, coOrds);
break;
case LOOK_KEY:
if (InventoryActive())
ButtonToInventory(BE_SRIGHT);
case PLR_LOOK:
REAL_ACTION_CHECK;
if (TinselV2 || !InventoryActive())
ProcessUserEvent(LOOK, coOrds, PLR_SRIGHT);
else
User_Event(LOOK, BE_NONE);
break;
case ESC_KEY:
case PGUP_KEY:
case PGDN_KEY:
case HOME_KEY:
case END_KEY:
if (InventoryActive())
KeyToInventory(ke);
EventToInventory(PLR_SRIGHT, coOrds);
break;
default:
if (InventoryActive())
EventToInventory(pEvent, coOrds);
break;
}
}
@ -407,7 +484,6 @@ void ProcessKeyEvent(KEYEVENT ke) {
/**
* For ESCapable Glitter sequences
*/
int GetEscEvents(void) {
return escEvents;
}
@ -415,15 +491,21 @@ int GetEscEvents(void) {
/**
* For cutting short talk()s etc.
*/
int GetLeftEvents(void) {
return butEvents;
return leftEvents;
}
bool LeftEventChange(int myleftEvent) {
if (leftEvents != myleftEvent) {
ProcessedProvisional();
return true;
} else
return false;
}
/**
* For waitkey() Glitter function
*/
int getUserEvents(void) {
return userEvents;
}
@ -436,4 +518,154 @@ void resetUserEventTime(void) {
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

View file

@ -29,49 +29,99 @@
#include "tinsel/dw.h"
#include "tinsel/coroutine.h"
#include "common/rect.h"
namespace Tinsel {
/*
enum BUTEVENT {
BE_NONE, BE_SLEFT, BE_DLEFT, BE_SRIGHT, BE_DRIGHT,
BE_LDSTART, BE_LDEND, BE_RDSTART, BE_RDEND,
BE_UNKNOWN
PLR_NOEVENT, PLR_SLEFT, PLR_DLEFT, PLR_SRIGHT, PLR_DRIGHT,
PLR_DRAG1_START, PLR_DRAG1_END, PLR_DRAG2_START, PLR_DRAG2_END,
PLR_UNKNOWN
};
enum KEYEVENT {
ESC_KEY, QUIT_KEY, SAVE_KEY, LOAD_KEY, OPTION_KEY,
PGUP_KEY, PGDN_KEY, HOME_KEY, END_KEY,
WALKTO_KEY, ACTION_KEY, LOOK_KEY,
PLR_ESCAPE, PLR_QUIT, PLR_SAVE, PLR_LOAD, PLR_MENU,
PLR_PGUP, PLR_PGDN, PLR_HOME, PLR_END,
PLR_WALKTO, PLR_ACTION, PLR_LOOK,
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.
* Do not re-order these as equivalent CONSTs are defined in the master
* 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 {
POINTED, WALKTO, ACTION, LOOK,
ENTER, LEAVE, STARTUP, CONVERSE,
UNPOINT, PUTDOWN,
NOEVENT
enum TINSEL_EVENT {
NOEVENT, STARTUP, CLOSEDOWN, POINTED, UNPOINT, WALKIN, WALKOUT,
PICKUP, PUTDOWN, WALKTO, LOOK, ACTION, CONVERSE, SHOWEVENT,
HIDEEVENT, TALKING, ENDTALK, LEAVE_T2, RESTORE, PROV_WALKTO
};
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(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 effRunPolyTinselCode(HPOLYGON hPoly, USER_EVENT event, int actor);
void RunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, PLR_EVENT be, bool tc);
void effRunPolyTinselCode(HPOLYGON hPoly, TINSEL_EVENT event, int actor);
void ProcessButEvent(BUTEVENT be);
void ProcessKeyEvent(KEYEVENT ke);
void ProcessButEvent(PLR_EVENT be);
void ProcessKeyEvent(PLR_EVENT ke);
int GetEscEvents(void);
int GetLeftEvents(void);
bool LeftEventChange(int myleftEvent);
int getUserEvents(void);
uint32 getUserEventTime(void);
@ -79,6 +129,16 @@ void resetUserEventTime(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
#endif /* TINSEL_EVENTS_H */

View file

@ -24,11 +24,14 @@
* Palette Fader and Flasher processes.
*/
#include "tinsel/pid.h" // list of all process IDs
#include "tinsel/sched.h" // scheduler defs
#include "tinsel/actors.h"
#include "tinsel/faders.h" // fader defs
#include "tinsel/handle.h"
#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 {
@ -40,9 +43,7 @@ struct FADE {
// fixed point fade multiplier tables
//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, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
/**
* 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) {
for (int i = 0; i < numColours; i++, pNew++, pOrig++) {
if (!TinselV2)
// apply multiplier to RGB components
*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);
// 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);
if (TinselV2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, true);
// get pointer to palette - reduce pointer indirection a bit
_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
// 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,
FROM_LE_32(_ctx->pPalette->numColours), (uint32) *_ctx->pColMult);
@ -110,6 +129,10 @@ static void FadeProcess(CORO_PARAM, const void *param) {
CORO_SLEEP(1);
}
if (TinselV2)
// Note that this palette is being faded
FadingPalette(pFade->pPalQ, false);
CORO_END_CODE;
}
@ -122,6 +145,13 @@ static void FadeProcess(CORO_PARAM, const void *param) {
static void Fader(const long multTable[], SCNHANDLE noFadeTable[]) {
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
for (pPal = GetNextPalette(NULL); pPal != NULL; pPal = GetNextPalette(pPal)) {
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.
* @param noFadeTable A NULL terminated list of palettes not to fade.
*/
void FadeOutFast(SCNHANDLE noFadeTable[]) {
// Fixed point fade multiplier table
static const long fadeout[] = {0xd000, 0xa000, 0x7000, 0x4000, 0x1000, 0, -1};
// call generic fader
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.
* @param noFadeTable A NULL terminated list of palettes not to fade.
*/
void FadeInFast(SCNHANDLE noFadeTable[]) {
// Fade multiplier table
static const long fadein[] = {0, 0x1000, 0x4000, 0x7000, 0xa000, 0xd000, 0x10000L, -1};
// call generic fader
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

View file

@ -28,16 +28,15 @@
#define TINSEL_FADERS_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/tinsel.h"
namespace Tinsel {
enum {
/**
/**
* 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 *|
@ -47,8 +46,11 @@ enum {
// should not be faded. This parameter can be
// NULL - fade all palettes.
void FadeOutMedium(SCNHANDLE noFadeTable[]);
void FadeOutFast(SCNHANDLE noFadeTable[]);
void FadeInMedium(SCNHANDLE noFadeTable[]);
void FadeInFast(SCNHANDLE noFadeTable[]);
void PokeInTagColour(void);
} // end of namespace Tinsel

View file

@ -22,11 +22,14 @@
* $Id$
*/
#include "tinsel/actors.h"
#include "tinsel/dw.h"
#include "tinsel/font.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
#include "tinsel/sysvar.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@ -35,48 +38,63 @@ namespace Tinsel {
static char tBuffer[TBUFSZ];
static SCNHANDLE hTagFont = 0, hTalkFont = 0;
static SCNHANDLE hRegularTalkFont = 0, hRegularTagFont = 0;
/**
* Return address of tBuffer
*/
char *tBufferAddr() {
char *TextBufferAddr() {
return tBuffer;
}
/**
* Return hTagFont handle.
*/
SCNHANDLE hTagFontHandle() {
SCNHANDLE GetTagFontHandle() {
return hTagFont;
}
/**
* Return hTalkFont handle.
*/
SCNHANDLE hTalkFontHandle() {
SCNHANDLE GetTalkFontHandle() {
return hTalkFont;
}
/**
* Called from dec_tagfont() Glitter function. Store the tag font handle.
*/
void TagFontHandle(SCNHANDLE hf) {
hTagFont = hf; // Store the font handle
void SetTagFontHandle(SCNHANDLE hFont) {
hTagFont = hRegularTagFont = hFont; // Store the font handle
}
/**
* Called from dec_talkfont() Glitter function.
* Store the talk font handle.
*/
void TalkFontHandle(SCNHANDLE hf) {
hTalkFont = hf; // Store the font handle
void SetTalkFontHandle(SCNHANDLE hFont) {
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.
*/
void fettleFontPal(SCNHANDLE fontPal) {
void FettleFontPal(SCNHANDLE fontPal) {
const FONT *pFont;
IMAGE *pImg;
@ -86,11 +104,23 @@ void fettleFontPal(SCNHANDLE fontPal) {
pFont = (const FONT *)LockMem(hTagFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
if (!TinselV2)
pImg->hImgPal = TO_LE_32(fontPal);
else
pImg->hImgPal = 0;
pFont = (const FONT *)LockMem(hTalkFont);
pImg = (IMAGE *)LockMem(FROM_LE_32(pFont->fontInit.hObjImg)); // get image for char 0
if (!TinselV2)
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

View file

@ -31,17 +31,27 @@
namespace Tinsel {
// 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
char *tBufferAddr(void);
SCNHANDLE hTagFontHandle(void);
SCNHANDLE hTalkFontHandle(void);
char *TextBufferAddr(void);
void TagFontHandle(SCNHANDLE hf);
void TalkFontHandle(SCNHANDLE hf);
void fettleFontPal(SCNHANDLE fontPal);
SCNHANDLE GetTagFontHandle(void);
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

View file

@ -28,6 +28,7 @@
#include "tinsel/handle.h" // LockMem()
#include "tinsel/object.h"
#include "tinsel/palette.h"
#include "tinsel/scene.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@ -46,7 +47,6 @@ extern uint8 transPalette[MAX_COLOURS];
/**
* Straight rendering with transparency support
*/
static void WrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP, bool applyClipping) {
// Set up the offset between destination blocks
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
*/
static void WrtConst(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) {
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
* lookup table from transpal.cpp (the contents of which have been moved into palette.cpp)
*/
static void WrtTrans(DRAWOBJECT *pObj, uint8 *destP, bool applyClipping) {
if (applyClipping) {
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
* Possibly only used in the Discworld Demo
* Copies an uncompressed block of data straight to the screen
*/
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) {
// FIXME: If this method is used for the demo, it still needs to be made Endian safe
if (applyClipping) {
srcP += (pObj->topClip * pObj->width) + pObj->leftClip;
// Set up the offset between destination lines
pObj->lineoffset = SCREEN_WIDTH - pObj->width - (applyClipping ? pObj->leftClip - pObj->rightClip : 0);
pObj->height -= pObj->topClip + pObj->botClip;
pObj->width -= pObj->leftClip + pObj->rightClip;
// Top clipped line handling
while (applyClipping && (pObj->topClip > 0)) {
// 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;
}
if (pObj->width <= 0)
return;
}
--pObj->height;
--pObj->topClip;
}
// Loop for the required number of rows
while (pObj->height > 0) {
int width = pObj->width;
// Handling for left edge clipping - this basically involves dumping data until we reach
// the part of the line to be displayed
int clipLeft = pObj->leftClip;
while (applyClipping && (clipLeft > 0)) {
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
srcP += sizeof(uint32);
if (opcodeOrLen >= 0) {
// Copy a specified number of bytes
// Make adjustments for past the clipping width
int remainder = 4 - (opcodeOrLen % 4);
srcP += MIN(clipLeft, opcodeOrLen);
opcodeOrLen -= MIN(clipLeft, opcodeOrLen);
clipLeft -= MIN(clipLeft, opcodeOrLen);
width -= opcodeOrLen;
// Handle any right edge clipping (if run length covers entire width)
if (width < pObj->rightClip) {
remainder += (pObj->rightClip - width);
opcodeOrLen -= (pObj->rightClip - width);
}
if (opcodeOrLen > 0)
Common::copy(srcP, srcP + opcodeOrLen, destP);
} else {
// Output a run length number of bytes
// Get data for byte value and run length
opcodeOrLen = -opcodeOrLen;
int runLength = opcodeOrLen & 0xff;
uint8 colourVal = (opcodeOrLen >> 8) & 0xff;
// Make adjustments for past the clipping width
runLength -= MIN(clipLeft, runLength);
clipLeft -= MIN(clipLeft, runLength);
width -= runLength;
// Handle any right edge clipping (if run length covers entire width)
if (width < pObj->rightClip)
runLength -= (pObj->rightClip - width);
if (runLength > 0) {
// Displayable part starts partway through the slice
if (colourVal != 0)
Common::set_to(destP, destP + runLength, colourVal);
destP += runLength;
}
}
if (width < pObj->rightClip)
width = 0;
}
// Handling for the visible part of the line
int endWidth = applyClipping ? pObj->rightClip : 0;
while (width > endWidth) {
int32 opcodeOrLen = (int32)READ_LE_UINT32(srcP);
srcP += sizeof(uint32);
if (opcodeOrLen >= 0) {
// Copy the specified number of bytes
int remainder = 4 - (opcodeOrLen % 4);
if (width < endWidth) {
// Shorten run length by right clipping
remainder += (pObj->rightClip - width);
opcodeOrLen -= (pObj->rightClip - width);
}
Common::copy(srcP, srcP + opcodeOrLen, destP);
srcP += opcodeOrLen + remainder;
destP += opcodeOrLen;
width -= opcodeOrLen;
} else {
// Handle a given run length
opcodeOrLen = -opcodeOrLen;
int runLength = opcodeOrLen & 0xff;
uint8 colourVal = (opcodeOrLen >> 8) & 0xff;
if (width < endWidth)
// Shorten run length by right clipping
runLength -= (pObj->rightClip - width);
// Only set pixels if colourVal non-zero (0 signifies transparency)
if (colourVal != 0)
// Fill out a run length of a specified colour
Common::set_to(destP, destP + runLength, colourVal);
destP += runLength;
width -= runLength;
}
}
// If right edge clipping is being applied, then width may still be non-zero - in
// 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);
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;
destP += pObj->lineoffset;
for (int y = 0; y < pObj->height; ++y) {
Common::copy(srcP, srcP + pObj->width, destP);
srcP += objWidth;
destP += SCREEN_WIDTH;
}
}
/**
* Renders a packed data stream with a variable sized palette
*/
static void PackedWrtNonZero(DRAWOBJECT *pObj, uint8 *srcP, uint8 *destP,
bool applyClipping, bool horizFlipped, int packingType) {
uint8 numColours = 0;
uint8 *colourTable = NULL;
int topClip = 0;
int xOffset = 0;
int numBytes, colour;
int v;
if (applyClipping) {
pObj->height -= pObj->botClip;
topClip = pObj->topClip;
}
if (packingType == 3) {
// Variable colours
numColours = *srcP++;
colourTable = srcP;
srcP += numColours;
}
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);
bool eolFlag = false;
// Get offset for first pixels in next line
xOffset = *srcP++;
int x = 0;
while (x < pObj->width) {
// Get next run size and colour to use
for (;;) {
if (xOffset > 0) {
x += xOffset;
// Reduce offset amount by any remaining left clipping
v = MIN(xOffset, leftClip);
xOffset -= v;
leftClip -= v;
if (horizFlipped) tempP -= xOffset; else tempP += xOffset;
xOffset = 0;
}
v = *srcP++;
numBytes = v & 0xf; // No. bytes 1-15
if (packingType == 3)
colour = colourTable[v >> 4];
else
colour = pObj->baseCol + (v >> 4);
if (numBytes != 0)
break;
numBytes = *srcP++;
if (numBytes >= 16)
break;
xOffset = numBytes + v;
if (xOffset == 0) {
// End of line encountered
eolFlag = true;
break;
}
}
if (eolFlag)
break;
// Apply clipping on byte sequence
v = MIN(numBytes, leftClip);
leftClip -= v;
numBytes -= v;
x += v;
while (numBytes-- > 0) {
if ((topClip == 0) && (x < (pObj->width - rightClip))) {
*tempP = colour;
if (horizFlipped) --tempP; else ++tempP;
}
++x;
}
}
assert(x <= pObj->width);
if (!eolFlag) {
// Assert that the next bytes signal a line end
assert((*srcP++ & 0xf) == 0);
assert(*srcP++ == 0);
}
if (topClip > 0)
--topClip;
else
destP += SCREEN_WIDTH;
}
}
#endif
//----------------- MAIN FUNCTIONS ---------------------
@ -380,8 +430,10 @@ void ClearScreen() {
* Updates the screen surface within the following rectangle
*/
void UpdateScreenRect(const Common::Rect &pClip) {
byte *pDest = (byte *)_vm->screen().getBasePtr(pClip.left, pClip.top);
g_system->copyRectToScreen(pDest, _vm->screen().pitch, pClip.left, pClip.top, pClip.width(), pClip.height());
int yOffset = (g_system->getHeight() - SCREEN_HEIGHT) / 2;
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();
}
@ -398,18 +450,76 @@ void DrawObject(DRAWOBJECT *pObj) {
// If writing constant data, don't bother locking the data pointer and reading src details
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->transOffset = READ_LE_UINT32(p + 0x14);
}
}
// Get destination starting point
destPtr = (byte *)_vm->screen().getBasePtr(pObj->xPos, pObj->yPos);
// Handle various draw types
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) {
case 0x01:
case 0x08:
@ -420,7 +530,7 @@ void DrawObject(DRAWOBJECT *pObj) {
case 0x04:
case 0x44:
// ClpWrtConst with/without clipping
// WrtConst with/without clipping
WrtConst(pObj,destPtr, typeId == 0x44);
break;
@ -435,6 +545,7 @@ void DrawObject(DRAWOBJECT *pObj) {
error("Unknown drawing type %d", typeId);
break;
}
}
}
} // End of namespace Tinsel

View file

@ -37,12 +37,6 @@ namespace Tinsel {
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 */
struct DRAWOBJECT {
char *charBase; // character set base address
@ -60,6 +54,7 @@ struct DRAWOBJECT {
int botClip; // amount to clip off object bottom
short xPos; // x 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 "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/scn.h" // name of "index" file
#include "tinsel/handle.h"
@ -41,6 +42,8 @@
#include "tinsel/object.h"
#include "tinsel/palette.h"
#include "tinsel/text.h"
#include "tinsel/timers.h"
#include "tinsel/tinsel.h"
#include "tinsel/scene.h"
namespace Tinsel {
@ -58,6 +61,7 @@ struct MEMHANDLE {
char szName[12]; //!< 00 - file name of graphics file
int32 filesize; //!< 12 - file size and flags
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 MALLOC_MASK 0xFF000000L //!< mask to isolate the memory allocation flags
#define OFFSETMASK 0x007fffffL //!< get offset of address
//#define HANDLEMASK 0xFF800000L //!< get handle of address
//----------------- LOCAL GLOBAL DATA --------------------
@ -83,6 +86,13 @@ static MEMHANDLE *handleTable = 0;
// number of handles in the handle table
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 --------------------
@ -94,7 +104,8 @@ static void LoadFile(MEMHANDLE *pH, bool bWarn); // load a memory block as a fil
* permanent graphics etc.
*/
void SetupHandleTable(void) {
enum { RECORD_SIZE = 20 };
bool t2Flag = (TinselVersion == TINSEL_V2);
int RECORD_SIZE = t2Flag ? 24 : 20;
int len;
uint i;
@ -108,7 +119,7 @@ void SetupHandleTable(void) {
if (len > 0) {
if ((len % RECORD_SIZE) != 0) {
// index file is corrupt
error("File %s is corrupt", INDEX_FILENAME);
error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
// calc number of handles
@ -128,31 +139,36 @@ void SetupHandleTable(void) {
// need to read that from the file.
handleTable[i].pNode = NULL;
f.seek(4, SEEK_CUR);
// For Discworld 2, read in the flags2 field
handleTable[i].flags2 = t2Flag ? f.readUint32LE() : 0;
}
if (f.ioFailed()) {
// index file is corrupt
error("File %s is corrupt", INDEX_FILENAME);
error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
// close the file
f.close();
} else { // index file is corrupt
error("File %s is corrupt", INDEX_FILENAME);
error(FILE_IS_CORRUPT, INDEX_FILENAME);
}
} 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
for (i = 0, pH = handleTable; i < numHandles; i++, pH++) {
if (pH->filesize & fPreload) {
// 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
assert(pH->pNode);
// Initialise the MEM_NODE structure
memset(pH->pNode, 0, sizeof(MEM_NODE));
// load the data
LoadFile(pH, true);
}
@ -178,8 +194,101 @@ void FreeHandleTable(void) {
free(handleTable);
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.
* @param pH Memory block pointer
@ -204,7 +313,7 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (pH->filesize & fPreload)
// preload - no need to lock the memory
addr = (uint8 *)pH->pNode;
addr = (uint8 *)pH->pNode + sizeof(MEM_NODE);
else {
// discardable - lock the memory
addr = (uint8 *)MemoryLock(pH->pNode);
@ -243,12 +352,12 @@ void LoadFile(MEMHANDLE *pH, bool bWarn) {
if (bWarn)
// file is corrupt
error("File %s is corrupt", szFilename);
error(FILE_IS_CORRUPT, szFilename);
}
if (bWarn)
// 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;
if (pH->filesize & fPreload) {
if (TinselV2)
// update the LRU time (new in this file)
pH->pNode->lruTime = DwGetCurrentTime();
// 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 {
if (pH->pNode->pBaseAddr && (pH->filesize & fLoaded))
// already allocated and loaded
@ -312,7 +452,10 @@ void LockScene(SCNHANDLE offset) {
if ((pH->filesize & fPreload) == 0) {
// 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
bLockedScene = true;
#endif
@ -363,4 +506,50 @@ bool ValidHandle(SCNHANDLE offset) {
}
#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

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
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
#endif // TINSEL_HANDLE_H

View file

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

View file

@ -24,6 +24,7 @@
* Functions to set up moving actors' reels.
*/
#include "tinsel/handle.h"
#include "tinsel/pcode.h" // For D_UP, D_DOWN
#include "tinsel/rince.h"
@ -34,7 +35,7 @@ namespace Tinsel {
//----------------- LOCAL GLOBAL DATA --------------------
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
MAX_SCRENTRIES = NUM_INTERVALS*2*3
@ -51,36 +52,80 @@ static SCIdataStruct SCIdata[MAX_SCRENTRIES];
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.
*/
SCNHANDLE GetMactorTalkReel(PMACTOR pActor, TFTYPE dirn) {
SCNHANDLE GetMoverTalkReel(PMOVER pActor, TFTYPE dirn) {
assert(1 <= pActor->scale && pActor->scale <= TOTAL_SCALES);
switch (dirn) {
case TF_NONE:
return pActor->TalkReels[pActor->scale-1][pActor->dirn];
return pActor->talkReels[pActor->scale-1][pActor->direction];
case TF_UP:
return pActor->TalkReels[pActor->scale-1][AWAY];
return pActor->talkReels[pActor->scale-1][AWAY];
case TF_DOWN:
return pActor->TalkReels[pActor->scale-1][FORWARD];
return pActor->talkReels[pActor->scale-1][FORWARD];
case TF_LEFT:
return pActor->TalkReels[pActor->scale-1][LEFTREEL];
return pActor->talkReels[pActor->scale-1][LEFTREEL];
case TF_RIGHT:
return pActor->TalkReels[pActor->scale-1][RIGHTREEL];
return pActor->talkReels[pActor->scale-1][RIGHTREEL];
default:
error("GetMactorTalkReel() - illegal direction!");
error("GetMoverTalkReel() - illegal direction!");
}
}
/**
* 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) {
assert(scale >= 1 && scale <= NUM_MAINSCALES); // invalid scale
assert(!(scale == 1 && direction == D_UP) &&
@ -101,7 +146,7 @@ void setscalingreels(int actor, int scale, int direction,
/**
* ScalingReel
*/
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRREEL reel) {
SCNHANDLE ScalingReel(int ano, int scale1, int scale2, DIRECTION reel) {
int d; // Direction
// The smaller the number, the bigger the scale
@ -129,4 +174,20 @@ void RebootScalingReels(void) {
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

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

File diff suppressed because it is too large Load diff

View file

@ -27,17 +27,24 @@
#define TINSEL_MOVE_H
#include "tinsel/dw.h" // for SCNHANDLE
#include "tinsel/rince.h"
namespace Tinsel {
struct MACTOR;
struct MOVER;
void SetActorDest(MACTOR *pActor, int x, int y, bool igPath, SCNHANDLE film);
void SSetActorDest(MACTOR *pActor);
void DoMoveActor(MACTOR *pActor);
typedef enum { YB_X2, YB_X1_5 } YBIAS;
int SetActorDest(MOVER *pMover, int x, int y, bool igPath, SCNHANDLE film);
void SSetActorDest(MOVER *pActor);
void DoMoveActor(MOVER *pMover);
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
#endif /* TINSEL_MOVE_H */

View file

@ -27,6 +27,7 @@
#include "tinsel/multiobj.h"
#include "tinsel/handle.h"
#include "tinsel/object.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@ -204,6 +205,8 @@ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
if (deltaX == 0 && deltaY == 0)
return; // ignore no change
if (!TinselV2) {
// *** This may be wrong!!!
if (pMultiObj->flags & DMA_FLIPH) {
// image is flipped horizontally - flip the x direction
deltaX = -deltaX;
@ -213,6 +216,7 @@ void MultiAdjustXY(OBJECT *pMultiObj, int deltaX, int deltaY) {
// image is flipped vertically - flip the y direction
deltaY = -deltaY;
}
}
// for all the objects that make up this multi-part
do {
@ -530,4 +534,32 @@ int MultiLowest(OBJECT *pMulti) {
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

View file

@ -28,6 +28,7 @@
#define TINSEL_MULTIOBJ_H
#include "tinsel/dw.h"
#include "tinsel/object.h"
namespace Tinsel {
@ -45,7 +46,9 @@ struct MULTI_INIT {
int32 mulX; //!< multi-objects initial x ani position
int32 mulY; //!< multi-objects initial y ani position
int32 mulZ; //!< multi-objects initial z position
uint32 otherFlags; //!< multi-objects Tinsel 2 - other flags
} PACKED_STRUCT;
typedef MULTI_INIT *PMULTI_INIT;
#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
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
#endif // TINSEL_MULTIOBJ_H

View file

@ -31,12 +31,21 @@
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/audiocd.h"
#include "sound/adpcm.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "tinsel/config.h"
#include "tinsel/sound.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 {
@ -153,8 +162,8 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
currentMidi = dwFileOffset;
currentLoop = bLoop;
if (volMidi != 0) {
SetMidiVolume(volMidi);
if (volMusic != 0) {
SetMidiVolume(volMusic);
// Support for compressed music from the music enhancement project
AudioCD.stop();
@ -191,7 +200,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
error("Cannot find file %s", MIDI_FILE);
error(CANNOT_FIND_FILE, MIDI_FILE);
// update index of last tune loaded
dwLastMidiIndex = dwMidiIndex;
@ -206,22 +215,22 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size);
// stop any currently playing tune
_vm->_music->stop();
_vm->_midiMusic->stop();
// read the sequence
if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen)
error("File %s is corrupt", MIDI_FILE);
error(FILE_IS_CORRUPT, MIDI_FILE);
midiStream.close();
_vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
_vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
// Store the length
dwLastSeqLen = dwSeqLen;
} else {
// dwMidiIndex == dwLastMidiIndex
_vm->_music->stop();
_vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
_vm->_midiMusic->stop();
_vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
}
// allow another sequence to play
@ -235,7 +244,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
*/
bool MidiPlaying(void) {
if (AudioCD.isPlaying()) return true;
return _vm->_music->isPlaying();
return _vm->_midiMusic->isPlaying();
}
/**
@ -246,7 +255,7 @@ bool StopMidi(void) {
currentLoop = false;
AudioCD.stop();
_vm->_music->stop();
_vm->_midiMusic->stop();
return true;
}
@ -255,7 +264,7 @@ bool StopMidi(void) {
* Gets the volume of the MIDI music.
*/
int GetMidiVolume() {
return volMidi;
return volMusic;
}
/**
@ -265,24 +274,24 @@ int GetMidiVolume() {
void SetMidiVolume(int vol) {
assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume);
if (vol == 0 && volMidi == 0) {
if (vol == 0 && volMusic == 0) {
// Nothing to do
} else if (vol == 0 && volMidi != 0) {
} else if (vol == 0 && volMusic != 0) {
// Stop current midi sequence
AudioCD.stop();
StopMidi();
} else if (vol != 0 && volMidi == 0) {
} else if (vol != 0 && volMusic == 0) {
// Perhaps restart last midi sequence
if (currentLoop) {
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
_vm->_music->setVolume(vol);
_vm->_midiMusic->setVolume(vol);
}
volMidi = vol;
volMusic = vol;
}
/**
@ -292,7 +301,7 @@ void OpenMidiFiles(void) {
Common::File midiStream;
// Demo version has no midi file
if (_vm->getFeatures() & GF_DEMO)
if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2))
return;
if (midiBuffer.pDat)
@ -301,12 +310,12 @@ void OpenMidiFiles(void) {
// open MIDI sequence file in binary mode
if (!midiStream.open(MIDI_FILE))
error("Cannot find file %s", MIDI_FILE);
error(CANNOT_FIND_FILE, MIDI_FILE);
// gen length of the largest sequence
midiBuffer.size = midiStream.readUint32LE();
if (midiStream.ioFailed())
error("File %s is corrupt", MIDI_FILE);
error(FILE_IS_CORRUPT, MIDI_FILE);
if (midiBuffer.size) {
// allocate a buffer big enough for the largest MIDI sequence
@ -327,14 +336,14 @@ void DeleteMidiBuffer() {
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));
_masterVolume = 0;
this->open();
_xmidiParser = MidiParser::createParser_XMIDI();
}
MusicPlayer::~MusicPlayer() {
MidiMusicPlayer::~MidiMusicPlayer() {
_driver->setTimerCallback(NULL, NULL);
stop();
this->close();
@ -342,7 +351,7 @@ MusicPlayer::~MusicPlayer() {
delete _xmidiParser;
}
void MusicPlayer::setVolume(int volume) {
void MidiMusicPlayer::setVolume(int volume) {
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, 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!
if (!_driver)
return 255;
@ -372,14 +381,14 @@ int MusicPlayer::open() {
return 0;
}
void MusicPlayer::close() {
void MidiMusicPlayer::close() {
stop();
if (_driver)
_driver->close();
_driver = 0;
}
void MusicPlayer::send(uint32 b) {
void MidiMusicPlayer::send(uint32 b) {
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// 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) {
case 0x2F: // End of Track
if (_looping)
@ -423,15 +432,15 @@ void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
}
}
void MusicPlayer::onTimer(void *refCon) {
MusicPlayer *music = (MusicPlayer *)refCon;
void MidiMusicPlayer::onTimer(void *refCon) {
MidiMusicPlayer *music = (MidiMusicPlayer *)refCon;
Common::StackLock lock(music->_mutex);
if (music->_isPlaying)
music->_parser->onTimer();
}
void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
if (_isPlaying)
return;
@ -463,7 +472,7 @@ void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) {
}
}
void MusicPlayer::stop() {
void MidiMusicPlayer::stop() {
Common::StackLock lock(_mutex);
_isPlaying = false;
@ -473,16 +482,414 @@ void MusicPlayer::stop() {
}
}
void MusicPlayer::pause() {
void MidiMusicPlayer::pause() {
setVolume(-1);
_isPlaying = false;
}
void MusicPlayer::resume() {
void MidiMusicPlayer::resume() {
setVolume(GetMidiVolume());
_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) {
*pMidi = currentMidi;
*pLoop = currentLoop;
@ -495,9 +902,9 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) {
currentMidi = Midi;
currentLoop = Loop;
if (volMidi != 0 && Loop) {
if (volMusic != 0 && Loop) {
PlayMidiSequence(currentMidi, true);
SetMidiVolume(volMidi);
SetMidiVolume(volMusic);
}
}
@ -546,4 +953,4 @@ void dumpMusic() {
}
#endif
} // End of namespace Made
} // End of namespace Tinsel

View file

@ -30,6 +30,8 @@
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/audiostream.h"
#include "sound/mixer.h"
#include "common/mutex.h"
namespace Tinsel {
@ -58,11 +60,10 @@ SCNHANDLE GetTrackOffset(int trackNumber);
void dumpMusic();
class MusicPlayer : public MidiDriver {
class MidiMusicPlayer : public MidiDriver {
public:
MusicPlayer(MidiDriver *driver);
~MusicPlayer();
MidiMusicPlayer(MidiDriver *driver);
~MidiMusicPlayer();
bool isPlaying() { return _isPlaying; }
void setPlaying(bool playing) { _isPlaying = playing; }
@ -71,6 +72,7 @@ public:
int getVolume() { return _masterVolume; }
void playXMIDI(byte *midiData, uint32 size, bool loop);
void stop();
void pause();
void resume();
@ -111,6 +113,93 @@ protected:
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

View file

@ -29,14 +29,13 @@
#include "tinsel/cliprect.h" // object clip rect defs
#include "tinsel/graphics.h" // low level interface
#include "tinsel/handle.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
#define OID_EFFECTS 0x2000 // generic special effects object id
namespace Tinsel {
/** screen clipping rectangle */
static const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// list of all objects
OBJECT *objectList = 0;
@ -194,6 +193,7 @@ void InsertObject(OBJECT *pObjList, OBJECT *pInsObj) {
*/
void DelObject(OBJECT *pObjList, OBJECT *pDelObj) {
OBJECT *pPrev, *pObj; // object list traversal pointers
const Common::Rect rcScreen(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// validate object pointer
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
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
// 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
// null image
@ -365,21 +367,25 @@ OBJECT *InitObject(const OBJ_INIT *pInitTbl) {
// get pointer to image
if (pInitTbl->hObjImg) {
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
if (pImg->hImgPal) {
// allocate a palette for this object
pPalQ = AllocPalette(FROM_LE_32(pImg->hImgPal));
// make sure palette allocated
assert(pPalQ != NULL);
}
// assign palette to object
pObj->pPal = pPalQ;
// set objects size
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
pObj->hBits = FROM_LE_32(pImg->hImgBits);
@ -434,7 +440,9 @@ void AnimateObjectFlags(OBJECT *pAniObj, int newflags, SCNHANDLE hNewImg) {
// setup new shape
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
pAniObj->hBits = FROM_LE_32(pNewImg->hImgBits);

View file

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

View file

@ -28,6 +28,7 @@
#include "tinsel/graphics.h"
#include "tinsel/handle.h" // LockMem definition
#include "tinsel/palette.h" // palette allocator structures etc.
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
#include "common/system.h"
@ -72,6 +73,17 @@ static VIDEO_DAC_Q *pDAChead;
/** the translucent palette lookup table */
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
// diagnostic palette counters
static int numPals = 0;
@ -243,6 +255,10 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
p->hPal = hNewPal; // set hardware palette data
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
// one more palette in use
if (++numPals > maxPals)
@ -250,6 +266,9 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
#endif
// Q the change to the video DAC
if (TinselV2)
UpdateDACqueue(p->posInDAC, pNewPal->numColours, p->palRGB);
else
UpdateDACqueueHandle(p->posInDAC, p->numColours, p->hPal);
// move all palettes after this one down (if necessary)
@ -265,9 +284,14 @@ PALQ *AllocPalette(SCNHANDLE hNewPal) {
+ pPrev->numColours | PALETTE_MOVED;
// Q the palette change in position to the video DAC
if (!TinselV2)
UpdateDACqueueHandle(pNxtPal->posInDAC,
pNxtPal->numColours,
pNxtPal->hPal);
else if (!pNxtPal->bFading)
UpdateDACqueue(pNxtPal->posInDAC,
pNxtPal->numColours,
pNxtPal->palRGB);
// update previous palette to current palette
pPrev = pNxtPal;
@ -347,10 +371,22 @@ void SwapPalette(PALQ *pPalQ, SCNHANDLE hNewPal) {
// install new palette
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
UpdateDACqueueHandle(pPalQ->posInDAC, FROM_LE_32(pNewPal->numColours), hNewPal);
}
} else {
// # colours are different - will have to update all following palette entries
assert(!TinselV2); // Fatal error for Tinsel 2
PALQ *pNxtPalQ; // next palette queue position
@ -410,6 +446,33 @@ void SetBgndColour(COLORREF colour) {
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.
* @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
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

View file

@ -43,7 +43,7 @@ enum {
MAX_COLOURS = 256, //!< maximum number of colours - 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
NUM_PALETTES = 3, //!< number of palettes
NUM_PALETTES = 32, //!< number of palettes
// Discworld has some fixed apportioned bits in the palette.
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 posInDAC; //!< palette position in the video DAC
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"
// field - the palette entry has moved
@ -137,8 +140,41 @@ COLORREF GetBgndColour(void); // returns current background colour
void SetBgndColour( // sets current background colour
COLORREF colour); // colour to set the background to
void FadingPalette(PPALQ pPalQ, bool bFading);
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
#endif // TINSEL_PALETTE_H

View file

@ -25,13 +25,16 @@
*/
#include "tinsel/dw.h"
#include "tinsel/drives.h"
#include "tinsel/events.h" // 'POINTED' etc.
#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/scn.h" // FindChunk()
#include "tinsel/serializer.h"
#include "tinsel/timers.h"
#include "tinsel/tinlib.h" // Library routines
#include "tinsel/tinsel.h"
#include "common/util.h"
@ -97,7 +100,7 @@ enum OPCODE {
#define OPMASK 0x3F //!< mask to isolate the opcode
bool bNoPause = false;
//----------------- LOCAL GLOBAL DATA --------------------
@ -107,13 +110,19 @@ static int numGlobals = 0; // How many global variables to save/restore
static INT_CONTEXT *icList = 0;
static uint32 hMasterScript;
/**
* Keeps the code array pointer up to date.
*/
void LockCode(INT_CONTEXT *ic) {
if (ic->GSort == GS_MASTER)
ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
if (ic->GSort == GS_MASTER) {
if (TinselV2)
// Get the srcipt handle from a specific global chunk
ic->code = (byte *)LockMem(hMasterScript);
else
ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
} else
ic->code = (byte *)LockMem(ic->hCode);
}
@ -124,7 +133,7 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
INT_CONTEXT *pic;
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 = g_scheduler->getCurrentProcess();
pic->GSort = gsort;
@ -141,11 +150,41 @@ static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
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.
* Called from the end of Interpret().
*/
static void FreeInterpretContextPi(INT_CONTEXT *pic) {
FreeWaitCheck(pic, true);
if (TinselV2)
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
}
@ -158,8 +197,11 @@ void FreeInterpretContextPr(PROCESS *pProc) {
INT_CONTEXT *pic;
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) {
FreeWaitCheck(pic, false);
if (TinselV2)
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
break;
}
@ -173,8 +215,9 @@ void FreeMostInterpretContexts(void) {
INT_CONTEXT *pic;
int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
if (pic->GSort != GS_MASTER) {
for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if ((pic->GSort != GS_MASTER) && (pic->GSort != GS_GPROCESS)) {
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
}
}
@ -187,8 +230,9 @@ void FreeMasterInterpretContext(void) {
INT_CONTEXT *pic;
int i;
for (i = 0, pic = icList; i < MAX_INTERPRET; i++, pic++) {
if (pic->GSort == GS_MASTER) {
for (i = 0, pic = icList; i < NUM_INTERPRET; i++, pic++) {
if ((pic->GSort == GS_MASTER) || (pic->GSort == GS_GPROCESS)) {
memset(pic, 0, sizeof(INT_CONTEXT));
pic->GSort = GS_NONE;
return;
}
@ -205,8 +249,8 @@ void FreeMasterInterpretContext(void) {
* @param actorId Associated actor (if any)
* @param pinvo Associated inventory object
*/
INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event,
HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo) {
INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, TINSEL_EVENT event,
HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
INT_CONTEXT *ic;
ic = AllocateInterpretContext(gsort);
@ -215,14 +259,14 @@ INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, USER_EVENT event
ic->hCode = hCode;
LockCode(ic);
ic->event = event;
ic->hpoly = hpoly;
ic->actorid = actorid;
ic->hPoly = hpoly;
ic->idActor = actorid;
ic->pinvo = pinvo;
// Previously local variables in Interpret()
ic->bHalt = false; // set to exit interpeter
ic->escOn = false;
ic->myescEvent = 0; // only initialised to prevent compiler warning!
ic->escOn = myEscape > 0;
ic->myEscape = myEscape;
ic->sp = 0;
ic->bp = ic->sp + 1;
ic->ip = 0; // start of code
@ -256,6 +300,9 @@ void RegisterGlobals(int num) {
if (pGlobals == NULL) {
numGlobals = num;
hMasterScript = !TinselV2 ? 0 :
READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));
// Allocate RAM for pGlobals and make sure it's allocated
pGlobals = (int32 *)calloc(numGlobals, sizeof(int32));
if (pGlobals == NULL) {
@ -263,18 +310,38 @@ void RegisterGlobals(int num) {
}
// 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) {
error("Cannot allocate memory for interpret contexts");
}
g_scheduler->setResourceCallback(FreeInterpretContextPr);
} else {
// Check size is still the same
assert(numGlobals == num);
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(hCode);
s.syncAsUint32LE(event);
s.syncAsSint32LE(hpoly);
s.syncAsSint32LE(actorid);
s.syncAsSint32LE(hPoly);
s.syncAsSint32LE(idActor);
for (int i = 0; i < PCODE_STACK_SIZE; ++i)
s.syncAsSint32LE(stack[i]);
@ -320,14 +387,14 @@ void INT_CONTEXT::syncWithSerializer(Serializer &s) {
s.syncAsSint32LE(ip);
s.syncAsUint32LE(bHalt);
s.syncAsUint32LE(escOn);
s.syncAsSint32LE(myescEvent);
s.syncAsSint32LE(myEscape);
}
/**
* Return pointer to and size of global data for save/restore game.
*/
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;
ic->sp += tmp2;
LockCode(ic);
if (TinselV2 && (ic->resumeState == RES_1))
ic->resumeState = RES_NOT;
break;
case OP_RET: // procedure return
@ -567,12 +636,14 @@ void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
break;
case OP_ESCON:
bNoPause = true;
ic->escOn = true;
ic->myescEvent = GetEscEvents();
ic->myEscape = GetEscEvents();
break;
case OP_ESCOFF:
ic->escOn = false;
ic->myEscape = 0;
break;
default:
@ -590,4 +661,128 @@ void Interpret(CORO_PARAM, INT_CONTEXT *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

View file

@ -27,7 +27,7 @@
#ifndef TINSEL_PCODE_H // prevent multiple includes
#define TINSEL_PCODE_H
#include "tinsel/events.h" // for USER_EVENT
#include "tinsel/events.h" // for TINSEL_EVENT
#include "tinsel/sched.h" // for PROCESS
namespace Tinsel {
@ -45,9 +45,12 @@ enum {
};
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 {
// Elements for interpret context management
@ -57,9 +60,9 @@ struct INT_CONTEXT {
// Previously parameters to Interpret()
SCNHANDLE hCode; //!< scene handle of the code to execute
byte *code; //!< pointer to the code to execute
USER_EVENT event; //!< causal event
HPOLYGON hpoly; //!< associated polygon (if any)
int actorid; //!< associated actor (if any)
TINSEL_EVENT event; //!< causal event
HPOLYGON hPoly; //!< associated polygon (if any)
int idActor; //!< associated actor (if any)
INV_OBJECT *pinvo; //!< associated inventory object
// Previously local variables in Interpret()
@ -69,27 +72,32 @@ struct INT_CONTEXT {
int ip; //!< instruction pointer
bool bHalt; //!< set to exit interpeter
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;
void syncWithSerializer(Serializer &s);
};
typedef INT_CONTEXT *PINT_CONTEXT;
/*----------------------------------------------------------------------*\
|* 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(
GSORT gsort,
SCNHANDLE hCode, // code to execute
USER_EVENT event, // causal event
TINSEL_EVENT event, // causal event
HPOLYGON hpoly, // associated polygon (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);
@ -101,8 +109,12 @@ void SaveInterpretContexts(INT_CONTEXT *sICInfo);
void RegisterGlobals(int num);
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 *|
@ -137,10 +149,6 @@ void FreeGlobals(void);
#define MIDI_DEF 0
#define MIDI_LOOP 1
#define TRANS_DEF 0
#define TRANS_CUT 1
#define TRANS_FADE 2
#define FM_IN 0 //
#define FM_OUT 1 // fademidi()

View file

@ -28,6 +28,7 @@
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/coroutine.h"
#include "tinsel/cursor.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
@ -41,6 +42,7 @@
#include "tinsel/sched.h"
#include "tinsel/strres.h"
#include "tinsel/text.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@ -56,8 +58,8 @@ extern int newestString; // The overrun counter, in STRRES.C
//----------------- EXTERNAL FUNCTIONS ---------------------
// in BG.C
extern int BackgroundWidth(void);
extern int BackgroundHeight(void);
extern int BgWidth(void);
extern int BgHeight(void);
@ -84,8 +86,11 @@ static bool bShowString = false;
static int TaggedActor = 0;
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
/**
@ -134,7 +139,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
char PositionString[64]; // sprintf() things into here
PMACTOR pActor; // Lead actor
PMOVER pActor; // Lead actor
while (1) {
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
@ -159,7 +164,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// New text objects
sprintf(PositionString, "%d %d", aniX + Loffset, aniY + Toffset);
_ctx->cpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, CPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
0, CPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
if (DispPath) {
HPOLYGON hp = InPolygon(aniX + Loffset, aniY + Toffset, PATH);
if (hp == NOPOLY)
@ -171,7 +176,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
PolyCornerX(hp, 2), PolyCornerY(hp, 2),
PolyCornerX(hp, 3), PolyCornerY(hp, 3));
_ctx->cpathText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, 4, POSY+ 10, hTagFontHandle(), 0);
0, 4, POSY+ 10, GetTagFontHandle(), 0);
}
// update previous position
@ -191,7 +196,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "%d", Overrun);
_ctx->opText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, OPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
0, OPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous value
_ctx->prevOver = Overrun;
@ -202,7 +207,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
| Lead actor's position. |
\*----------------------*/
pActor = GetMover(LEAD_ACTOR);
if (pActor && pActor->MActorState == NORM_MACTOR) {
if (pActor && pActor->MActorState == NORM_MOVER) {
// get lead's animation position
GetActorPos(LEAD_ACTOR, &aniX, &aniY);
@ -217,7 +222,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
// create new text object list
sprintf(PositionString, "%d %d", aniX, aniY);
_ctx->rpText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, LPOSX, POSY, hTagFontHandle(), TXT_CENTRE);
0, LPOSX, POSY, GetTagFontHandle(), TXT_CENTRE);
// update previous position
_ctx->prevlX = aniX;
@ -236,7 +241,7 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
sprintf(PositionString, "String: %d", newestString);
_ctx->spText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), PositionString,
0, SPOSX, POSY+10, hTalkFontHandle(), TXT_CENTRE);
0, SPOSX, POSY+10, GetTalkFontHandle(), TXT_CENTRE);
// update previous value
_ctx->prevString = newestString;
@ -252,9 +257,52 @@ void CursorPositionProcess(CORO_PARAM, const void *) {
}
#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
* (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) {
TaggedActor = ano;
@ -262,7 +310,7 @@ static void SaveTaggedActor(int ano) {
/**
* 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) {
return TaggedActor;
@ -270,7 +318,7 @@ int GetTaggedActor(void) {
/**
* 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) {
hTaggedPolygon = hp;
@ -341,12 +389,59 @@ static bool InHotSpot(int ano, int aniX, int aniY, int *pxtext, int *pytext) {
* the screen.
*/
static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
static int Loffset = 0, Toffset = 0; // Values when tag was displayed
int nLoff, nToff; // new values, to keep tag in place
static int tagX = 0, tagY = 0; // Values when tag was displayed
int newX, newY; // new values, to keep tag in place
int ano;
int xtext, ytext;
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....
FirstTaggedActor();
while ((ano = NextTaggedActor()) != 0) {
@ -369,20 +464,20 @@ static bool ActorTag(int curX, int curY, HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedActor(ano); // This actor tagged
SaveTaggedPoly(NOPOLY); // No tagged polygon
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
LoadStringRes(GetActorTag(ano), tBufferAddr(), TBUFSZ);
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
0, xtext - Loffset, ytext - Toffset, hTagFontHandle(), TXT_CENTRE);
PlayfieldGetPos(FIELD_WORLD, &tagX, &tagY);
LoadStringRes(GetActorTag(ano), TextBufferAddr(), TBUFSZ);
*ppText = ObjectTextOut(nullContext, GetPlayfieldList(FIELD_STATUS), TextBufferAddr(),
0, xtext - tagX, ytext - tagY, GetTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Actor tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
} else {
// Maintain actor tag's position
PlayfieldGetPos(FIELD_WORLD, &nLoff, &nToff);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
Loffset = nLoff;
Toffset = nToff;
PlayfieldGetPos(FIELD_WORLD, &newX, &newY);
if (newX != tagX || newY != tagY) {
MultiMoveRelXY(*ppText, tagX - newX, tagY - newY);
tagX = newX;
tagY = newY;
}
}
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 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
HPOLYGON hp;
bool newPoly;
@ -418,8 +514,11 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
for (int i = 0; i < MAX_POLY; i++) {
hp = GetPolyHandle(i);
if (TinselV2 && (hp == NOPOLY))
continue;
// 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
if (hp != GetTaggedPoly()) {
if (*ppText) {
@ -431,58 +530,99 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
SaveTaggedPoly(hp); // This polygon tagged
}
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
newPoly = false;
if (TinselV2) {
if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
} else {
if (*pTag != POLY_HOTSPOT_TAG)
newPoly = true; // A new polygon (no current)
else if (hp != GetTaggedPoly())
newPoly = true; // Different polygon
else
newPoly = false; // Same polygon
}
if (newPoly) {
if (*ppText)
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), *ppText);
if (!TinselV2)
*pTag = POLY_HOTSPOT_TAG;
SaveTaggedActor(0); // No tagged actor
SaveTaggedPoly(hp); // This polygon tagged
PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
getPolyTagInfo(hp, &hTagtext, &tagx, &tagy);
GetTagTag(hp, &hTagtext, &tagx, &tagy);
int strLen;
if (PolyTagHandle(hp) != 0)
strLen = LoadStringRes(PolyTagHandle(hp), tBufferAddr(), TBUFSZ);
if (GetPolyTagHandle(hp) != 0)
strLen = LoadStringRes(GetPolyTagHandle(hp), TextBufferAddr(), TBUFSZ);
else
strLen = LoadStringRes(hTagtext, tBufferAddr(), TBUFSZ);
strLen = LoadStringRes(hTagtext, TextBufferAddr(), TBUFSZ);
if (strLen == 0)
// No valid string returned, so leave ppText as NULL
ppText = NULL;
else {
// Handle displaying the tag text on-screen
*ppText = ObjectTextOut(GetPlayfieldList(FIELD_STATUS), tBufferAddr(),
0, tagx - Loffset, tagy - Toffset,
hTagFontHandle(), TXT_CENTRE);
assert(*ppText); // Polygon tag string produced NULL text
MultiSetZPosition(*ppText, Z_TAG_TEXT);
else if (TinselV2 && !PolyTagFollowsCursor(hp)) {
// May have buggered cursor
EndCursorFollowed();
*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
*/
shift = MultiRightmost(*ppText) + Loffset + 2;
if (shift >= BackgroundWidth()) // Not off right
MultiMoveRelXY(*ppText, BackgroundWidth() - shift, 0);
if (shift >= BgWidth()) // Not off right
MultiMoveRelXY(*ppText, BgWidth() - shift, 0);
shift = MultiLeftmost(*ppText) + Loffset - 1;
if (shift <= 0) // Not off left
MultiMoveRelXY(*ppText, -shift, 0);
shift = MultiLowest(*ppText) + Toffset;
if (shift > BackgroundHeight()) // Not off bottom
MultiMoveRelXY(*ppText, 0, BackgroundHeight() - shift);
if (shift > BgHeight()) // Not off bottom
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 {
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);
if (nLoff != Loffset || nToff != Toffset) {
MultiMoveRelXY(*ppText, Loffset - nLoff, Toffset - nToff);
@ -495,7 +635,9 @@ static bool PolyTag(HotSpotTag *pTag, OBJECT **ppText) {
}
// No tagged polygon
if (*pTag == POLY_HOTSPOT_TAG) {
if (TinselV2)
SaveTaggedPoly(NOPOLY);
else if (*pTag == POLY_HOTSPOT_TAG) {
*pTag = NO_HOTSPOT_TAG;
SaveTaggedPoly(NOPOLY);
}
@ -522,7 +664,7 @@ void TagProcess(CORO_PARAM, const void *) {
SaveTaggedPoly(NOPOLY); // No tagged polygon yet
while (1) {
if (TagsActive == TAGS_ON) {
if (bTagsActive) {
int curX, curY; // cursor position
while (!GetCursorXYNoWait(&curX, &curY, true))
CORO_SLEEP(1);
@ -533,6 +675,10 @@ void TagProcess(CORO_PARAM, const void *) {
if (_ctx->pText) {
MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), _ctx->pText);
_ctx->pText = NULL;
if (TinselV2)
// May have buggered cursor
EndCursorFollowed();
}
}
} else {
@ -557,22 +703,43 @@ void TagProcess(CORO_PARAM, const void *) {
/**
* Called from PointProcess() as appropriate.
*/
static void enteringpoly(HPOLYGON hp) {
SetPolyPointState(hp, POINTING);
static void enteringpoly(CORO_PARAM, HPOLYGON hp) {
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.
*/
static void leavingpoly(HPOLYGON hp) {
SetPolyPointState(hp, NOT_POINTING);
static void leavingpoly(CORO_PARAM, HPOLYGON hp) {
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
SetPolyTagState(hp, TAG_OFF);
}
CORO_END_CODE;
}
/**
@ -583,56 +750,95 @@ static void leavingpoly(HPOLYGON hp) {
void PointProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
HPOLYGON hPoly;
int i;
int curX, curY; // cursor/tagged actor position
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
if (TinselV2)
EnablePointing();
while (1) {
int aniX, aniY; // cursor/tagged actor position
while (!GetCursorXYNoWait(&aniX, &aniY, true))
while (!GetCursorXYNoWait(&_ctx->curX, &_ctx->curY, true))
CORO_SLEEP(1);
/*----------------------------------*\
| For polygons of type TAG and EXIT. |
\*----------------------------------*/
for (int i = 0; i < MAX_POLY; i++) {
HPOLYGON hp = GetPolyHandle(i);
for (_ctx->i = 0; _ctx->i < MAX_POLY; _ctx->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 (PolyPointState(hp) == NOT_POINTING) {
if (IsInPolygon(aniX, aniY, hp)) {
enteringpoly(hp);
if (!PolyIsPointedTo(_ctx->hPoly)) {
if (IsInPolygon(_ctx->curX, _ctx->curY, _ctx->hPoly)) {
if (TinselV2) {
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
do {
CORO_SLEEP(1);
} while (!bPointingActive);
} else {
// allow re-scheduling
CORO_SLEEP(1);
}
}
CORO_END_CODE;
}
void DisableTags(void) {
TagsActive = TAGS_OFF;
bTagsActive = false;
}
void EnableTags(void) {
TagsActive = TAGS_ON;
bTagsActive = true;
}
bool DisableTagsIfEnabled(void) {
if (TagsActive == TAGS_OFF)
return false;
else {
TagsActive = TAGS_OFF;
if (bTagsActive) {
DisableTags();
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_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_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
#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
enum PTYPE {
TEST, PATH, EXIT, BLOCKING,
EFFECT, REFER, TAG, EX_TAG, EX_EXIT, EX_BLOCK
// Tinsel 2 Polygon type list
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
@ -45,6 +49,13 @@ enum {
NODE = 1 // For paths
};
// tagFlags
enum {
POINTING = 0x01,
TAGWANTED = 0x02,
FOLLOWCURSOR = 0x04
};
// tagState
enum TSTATE {
TAG_OFF, TAG_ON
@ -52,7 +63,7 @@ enum TSTATE {
// pointState
enum PSTATE {
NO_POINT, NOT_POINTING, POINTING
PS_NO_POINT, PS_NOT_POINTING, PS_POINTING
};
@ -60,7 +71,10 @@ enum {
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 FindBestPoint(HPOLYGON path, int *x, int *y, int *line);
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 NearEndNode(HPOLYGON spath, HPOLYGON dpath);
int NearestNodeWithin(HPOLYGON npath, int x, int y);
void NearestCorner(int *x, int *y, HPOLYGON spath, HPOLYGON dpath);
bool IsPolyCorner(HPOLYGON hPath, int x, 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 getPolyTagInfo(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy);
SCNHANDLE getPolyFilm(HPOLYGON p);
void getPolyNode(HPOLYGON p, int *px, int *py);
SCNHANDLE getPolyScript(HPOLYGON p);
REEL getPolyReelType(HPOLYGON p);
int32 getPolyZfactor(HPOLYGON p);
void GetTagTag(HPOLYGON p, SCNHANDLE *hTagText, int *tagx, int *tagy);
SCNHANDLE GetPolyFilm(HPOLYGON p);
void GetPolyNode(HPOLYGON hp, int *pNodeX, int *pNodeY);
SCNHANDLE GetPolyScript(HPOLYGON p);
REEL GetPolyReelType(HPOLYGON p);
int32 GetPolyZfactor(HPOLYGON p);
int numNodes(HPOLYGON pp);
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 EnableBlock(int blockno);
void DisableTag(int tagno);
void EnableTag(int tagno);
HPOLYGON GetTagHandle(int tagno);
void DisableTag(CORO_PARAM, int tag);
void EnableTag(CORO_PARAM, int tag);
void DisableExit(int exitno);
void EnableExit(int exitno);
HPOLYGON FirstPathPoly(void);
@ -99,6 +123,8 @@ void DropPolygons(void);
void SaveDeadPolys(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]
PSTATE PolyPointState(HPOLYGON hp); // ->pointState
TSTATE PolyTagState(HPOLYGON hp); // ->tagState
SCNHANDLE PolyTagHandle(HPOLYGON hp); // ->oTagHandle
void SetPolyPointState(HPOLYGON hp, PSTATE ps); // ->pointState
void SetPolyTagState(HPOLYGON hp, TSTATE ts); // ->tagState
@ -118,6 +143,21 @@ void SetPolyTagHandle(HPOLYGON hp, SCNHANDLE th);// ->oTagHandle
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

File diff suppressed because it is too large Load diff

View file

@ -29,6 +29,7 @@
#include "tinsel/anim.h" // for ANIM
#include "tinsel/scene.h" // for TFTYPE
#include "tinsel/tinsel.h"
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 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 {
NUM_MAINSCALES = 5,
NUM_AUXSCALES = 5,
TOTAL_SCALES = NUM_MAINSCALES + NUM_AUXSCALES
};
#define BOGUS_BRIGHTNESS -1
struct MACTOR {
struct MOVER {
int objx; /* Co-ordinates object */
int objy;
int objX, objY; /* Co-ordinates object */
int targetX, targetY;
int ItargetX, ItargetY; /* Intermediate destination */
HPOLYGON hIpath;
int UtargetX, UtargetY; /* Ultimate destination */
HPOLYGON hUpath;
HPOLYGON hIpath;
HPOLYGON hUpath;
HPOLYGON hCpath;
bool over;
int ticket;
int walkNumber;
IND InDifficulty;
@ -74,129 +74,147 @@ struct MACTOR {
int Tline; // NEW
bool TagReelRunning;
// TODO: TagReelRunning may be the same as bSpecReel
bool bSpecReel;
/* Used internally */
DIRREEL dirn; // Current reel
DIRECTION direction; // Current reel
int scale; // Current scale
int scount; // Step count for walking reel synchronisation
unsigned fromx;
unsigned fromy;
int stepCount; // Step count for walking reel synchronisation
int walkedFromX, walkedFromY;
bool bMoving; // Set this to TRUE during a walk
bool bNoPath;
bool bIgPath;
bool walkReel;
bool bWalkReel;
OBJECT *actorObj; // Actor's object
ANIM actorAnim; // Actor's animation script
SCNHANDLE lastfilm; // } Used by AlterActor()
SCNHANDLE pushedfilm; // }
SCNHANDLE hLastFilm; // } Used by AlterMover()
SCNHANDLE hPushedFilm; // }
int actorID;
int actorToken;
SCNHANDLE WalkReels[TOTAL_SCALES][4];
SCNHANDLE StandReels[TOTAL_SCALES][4];
SCNHANDLE TalkReels[TOTAL_SCALES][4];
SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE standReels[REQ_TOTAL_SCALES][4];
SCNHANDLE talkReels[REQ_TOTAL_SCALES][4];
MAS MActorState;
bool bActive;
bool aHidden;
int SlowFactor; // Slow down movement while hidden
bool stop;
bool bStop;
/* NOTE: If effect polys can overlap, this needs improving */
bool InEffect;
bool bInEffect;
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 };
void StoreMoverPalette(PMOVER pMover, int startColour, int length);
MACTOR *GetMover(int ano);
MACTOR *SetMover(int ano);
void KillMActor(MACTOR *pActor);
MACTOR *GetLiveMover(int index);
void MoverBrightness(PMOVER pMover, int brightness);
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 getMActorHideState(MACTOR *pActor);
void unhideMActor(MACTOR *pActor);
void DropMActors(void);
void MoveMActor(MACTOR *pActor, int x, int y);
bool getMActorState(MOVER *psActor);
int GetMoverId(PMOVER pMover);
void SetMoverZ(PMOVER pMover, int y, uint32 zFactor);
void SetMoverZoverride(PMOVER pMover, uint32 zFactor);
void GetMActorPosition(MACTOR *pActor, int *aniX, int *aniY);
void GetMActorMidTopPosition(MACTOR *pActor, int *aniX, int *aniY);
int GetMActorLeft(MACTOR *pActor);
int GetMActorRight(MACTOR *pActor);
void HideMover(PMOVER pMover, int sf = 0);
bool MoverHidden(PMOVER pMover);
bool MoverIs(PMOVER pMover);
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 AlterMActor(MACTOR *actor, SCNHANDLE film, AR_FUNCTION fn);
DIRREEL GetMActorDirection(MACTOR *pActor);
int GetMActorScale(MACTOR *pActor);
void SetMActorDirection(MACTOR *pActor, DIRREEL dirn);
void SetMActorStanding(MACTOR *actor);
void SetMActorWalkReel(MACTOR *actor, DIRREEL reel, int scale, bool force);
void GetMoverPosition(PMOVER pMover, int *aniX, int *aniY);
void GetMoverMidTop(PMOVER pMover, int *aniX, int *aniY);
int GetMoverLeft(PMOVER pMover);
int GetMoverRight(PMOVER pMover);
int GetMoverTop(PMOVER pMover);
int GetMoverBottom(PMOVER pMover);
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);
bool IsMAinEffectPoly(int index);
void SetMAinEffectPoly(int index, bool tf);
void SetMoverInEffect(int index, bool tf);
bool MAmoving(MACTOR *pActor);
int GetActorTicket(MACTOR *pActor);
void StopMover(PMOVER pMover);
/*----------------------------------------------------------------------*/
struct SAVED_MOVER {
MAS MActorState;
int actorID;
int objx;
int objy;
SCNHANDLE lastfilm;
int objX;
int objY;
SCNHANDLE hLastfilm;
SCNHANDLE WalkReels[TOTAL_SCALES][4];
SCNHANDLE StandReels[TOTAL_SCALES][4];
SCNHANDLE TalkReels[TOTAL_SCALES][4];
SCNHANDLE walkReels[REQ_TOTAL_SCALES][4];
SCNHANDLE standReels[REQ_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 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 {
MAGICX = -101,
MAGICY = -102

View file

@ -25,8 +25,9 @@
*/
#include "tinsel/actors.h"
#include "tinsel/dialogs.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/inventory.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
#include "tinsel/serializer.h"
@ -63,7 +64,11 @@ namespace Tinsel {
#define VER(x) x
//----------------- GLOBAL GLOBAL DATA --------------------
int thingHeld = 0;
int restoreCD = 0;
SRSTATE SRstate = SR_IDLE;
//----------------- EXTERN FUNCTIONS --------------------
@ -79,9 +84,6 @@ extern void syncGlobInfo(Serializer &s);
// in POLYGONS.C
extern void syncPolyInfo(Serializer &s);
// in SAVESCN.CPP
extern void RestoreScene(SAVED_DATA *sd, bool bFadeOut);
//----------------- LOCAL DEFINES --------------------
struct SaveGameHeader {
@ -93,15 +95,17 @@ struct SaveGameHeader {
};
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
};
#define SAVEGAME_ID (TinselV2 ? (uint32)DW2_SAVEGAME_ID : (uint32)DW1_SAVEGAME_ID)
//----------------- LOCAL GLOBAL DATA --------------------
static int numSfiles = 0;
static SFILES savedFiles[MAX_SFILES];
static SFILES savedFiles[MAX_SAVED_FILES];
static bool NeedLoad = true;
@ -112,8 +116,6 @@ static const char *SaveSceneDesc = 0;
static int *SaveSceneSsCount = 0;
static char *SaveSceneSsData = 0; // points to 'SAVED_DATA ssdata[MAX_NEST]'
static SRSTATE SRstate = SR_IDLE;
//------------- SAVE/LOAD SUPPORT METHODS ----------------
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) {
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.objx);
s.syncAsSint32LE(sm.objy);
s.syncAsUint32LE(sm.lastfilm);
s.syncAsSint32LE(sm.objX);
s.syncAsSint32LE(sm.objY);
s.syncAsUint32LE(sm.hLastfilm);
for (int pIndex = 0; pIndex < 3; ++pIndex) {
SCNHANDLE *p = pList[pIndex];
for (int i = 0; i < TOTAL_SCALES * 4; ++i)
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) {
s.syncAsUint16LE(sa.actorID);
s.syncAsUint16LE(sa.z);
s.syncAsUint16LE(sa.zFactor);
s.syncAsUint32LE(sa.bAlive);
s.syncAsUint32LE(sa.presFilm);
s.syncAsUint16LE(sa.presRnum);
s.syncAsUint16LE(sa.presX);
s.syncAsUint16LE(sa.presY);
s.syncAsUint16LE(sa.presPlayX);
s.syncAsUint16LE(sa.presPlayY);
}
extern void syncAllActorsAlive(Serializer &s);
@ -186,6 +197,24 @@ static void syncNoScrollB(Serializer &s, NOSCROLLB &ns) {
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) {
s.syncAsUint32LE(sd.SavedSceneHandle);
s.syncAsUint32LE(sd.SavedBgroundHandle);
@ -197,7 +226,7 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
s.syncAsSint32LE(sd.NumSavedActors);
s.syncAsSint32LE(sd.SavedLoffset);
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);
for (int i = 0; i < MAX_POLY; ++i)
s.syncAsUint32LE(sd.SavedDeadPolys[i]);
@ -213,6 +242,32 @@ static void syncSavedData(Serializer &s, SAVED_DATA &sd) {
syncNoScrollB(s, sd.SavedNoScrollData.NoHScroll[i]);
s.syncAsUint32LE(sd.SavedNoScrollData.NumNoV);
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).
*/
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;
const Common::String pattern = target + ".???";
@ -255,7 +315,7 @@ int getList(Common::SaveFileManager *saveFileMan, const Common::String &target)
numSfiles = 0;
for (Common::StringList::const_iterator file = files.begin(); file != files.end(); ++file) {
if (numSfiles >= MAX_SFILES)
if (numSfiles >= MAX_SAVED_FILES)
break;
const Common::String &fname = *file;
@ -308,7 +368,6 @@ int getList(void) {
return getList(_vm->getSaveFileMan(), _vm->getTargetName());
}
char *ListEntry(int i, letype which) {
if (i == -1)
i = numSfiles;
@ -322,7 +381,16 @@ char *ListEntry(int i, letype which) {
}
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);
syncGlobInfo(s); // Glitter globals
@ -332,10 +400,15 @@ static void DoSync(Serializer &s) {
if (s.isSaving())
sg = WhichItemHeld();
s.syncAsSint32LE(sg);
if (s.isLoading())
if (s.isLoading()) {
if (TinselV2)
thingHeld = sg;
else
HoldItem(sg);
}
syncTimerInfo(s); // Timer data
if (!TinselV2)
syncPolyInfo(s); // Dead polygon data
syncSCdata(s); // Hook Scene and delayed scene
@ -347,6 +420,7 @@ static void DoSync(Serializer &s) {
syncSavedData(s, *sdPtr);
}
if (!TinselV2)
syncAllActorsAlive(s);
}
@ -440,7 +514,7 @@ void ProcessSRQueue(void) {
switch (SRstate) {
case SR_DORESTORE:
if (DoRestore()) {
RestoreScene(srsd, false);
DoRestoreScene(srsd, false);
}
SRstate = SR_IDLE;
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) {
if (TinselV2) {
if (num == -1)
return;
else if (num == -2) {
// From CD change for restore
num = RestoreGameNumber;
}
}
assert(num >= 0);
RestoreGameNumber = num;
@ -476,4 +559,14 @@ void RequestRestoreGame(int num, SAVED_DATA *sd, int *pSsCount, SAVED_DATA *pSsD
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

View file

@ -28,19 +28,24 @@
#include "tinsel/actors.h"
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/faders.h" // FadeOutFast()
#include "tinsel/graphics.h" // ClearScreen()
#include "tinsel/handle.h"
#include "tinsel/inventory.h"
#include "tinsel/heapmem.h"
#include "tinsel/dialogs.h"
#include "tinsel/music.h"
#include "tinsel/pid.h"
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/rince.h"
#include "tinsel/savescn.h"
#include "tinsel/scene.h"
#include "tinsel/sched.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinlib.h"
#include "tinsel/token.h"
@ -49,7 +54,6 @@ namespace Tinsel {
//----------------- EXTERN FUNCTIONS --------------------
// in BG.C
extern void startupBackground(SCNHANDLE bfilm);
extern SCNHANDLE GetBgroundHandle(void);
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)
void RestoreProcess(INT_CONTEXT *pic);
// in PLAY.C
extern void playThisReel(SCNHANDLE film, short reelnum, short z, int x, int y);
// in SCENE.C
extern SCNHANDLE GetSceneHandle(void);
extern void NewScene(SCNHANDLE scene, int entry);
//----------------- LOCAL DEFINES --------------------
@ -77,6 +75,11 @@ enum {
MAX_NEST = 4
};
//----------------- EXTERNAL GLOBAL DATA --------------------
extern int thingHeld;
extern int restoreCD;
extern SRSTATE SRstate;
//----------------- LOCAL GLOBAL DATA --------------------
@ -84,54 +87,52 @@ static bool ASceneIsSaved = false;
static int savedSceneCount = 0;
//static SAVED_DATA s_ssData[MAX_NEST];
static SAVED_DATA *s_ssData = 0;
static bool bNotDoneYet = false;
//static SAVED_DATA ssData[MAX_NEST];
static SAVED_DATA *ssData = NULL;
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;
//----------------- 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.
* @param sd Pointer to the scene data
*/
void SaveScene(SAVED_DATA *sd) {
void DoSaveScene(SAVED_DATA *sd) {
sd->SavedSceneHandle = GetSceneHandle();
sd->SavedBgroundHandle = GetBgroundHandle();
SaveMovers(sd->SavedMoverInfo);
sd->NumSavedActors = SaveActors(sd->SavedActorInfo);
PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
SaveInterpretContexts(sd->SavedICInfo);
SaveDeadPolys(sd->SavedDeadPolys);
sd->SavedControl = TestToken(TOKEN_CONTROL);
CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
sd->SavedNoBlocking = bNoBlocking;
sd->SavedControl = ControlIsOn();
sd->SavedNoBlocking = GetNoBlocking();
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;
}
@ -140,13 +141,32 @@ void SaveScene(SAVED_DATA *sd) {
* @param sd Pointer to the scene data
* @param bFadeOut Flag to perform a fade out
*/
void RestoreScene(SAVED_DATA *sd, bool bFadeOut) {
s_rsd = sd;
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
rsd = sd;
if (bFadeOut)
s_restoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
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.
* Also 'stand' all the moving actors at their saved positions.
*/
void sortActors(SAVED_DATA *rsd) {
for (int i = 0; i < rsd->NumSavedActors; i++) {
ActorsLife(rsd->SavedActorInfo[i].actorID, rsd->SavedActorInfo[i].bAlive);
void sortActors(SAVED_DATA *sd) {
assert(!TinselV2);
for (int i = 0; i < sd->NumSavedActors; i++) {
ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
// Should be playing the same reel.
if (rsd->SavedActorInfo[i].presFilm != 0) {
if (!actorAlive(rsd->SavedActorInfo[i].actorID))
if (sd->SavedActorInfo[i].presFilm != 0) {
if (!actorAlive(sd->SavedActorInfo[i].actorID))
continue;
playThisReel(rsd->SavedActorInfo[i].presFilm, rsd->SavedActorInfo[i].presRnum, rsd->SavedActorInfo[i].z,
rsd->SavedActorInfo[i].presX, rsd->SavedActorInfo[i].presY);
RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
}
}
RestoreAuxScales(rsd->SavedMoverInfo);
RestoreAuxScales(sd->SavedMoverInfo);
for (int i = 0; i < MAX_MOVERS; i++) {
if (rsd->SavedMoverInfo[i].MActorState == NORM_MACTOR)
stand(rsd->SavedMoverInfo[i].actorID, rsd->SavedMoverInfo[i].objx,
rsd->SavedMoverInfo[i].objy, rsd->SavedMoverInfo[i].lastfilm);
if (sd->SavedMoverInfo[i].bActive)
Stand(nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
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
if (rsd == &sgData) {
if (!TinselV2 && (rsd == &sgData)) {
g_scheduler->killMatchingProcess(PID_MASTER_SCR, -1);
FreeMasterInterpretContext();
}
for (int i = 0; i < MAX_INTERPRET; i++) {
for (int i = 0; i < NUM_INTERPRET; i++) {
switch (rsd->SavedICInfo[i].GSort) {
case GS_NONE:
break;
@ -203,14 +268,31 @@ void ResumeInterprets(SAVED_DATA *rsd) {
RestoreMasterProcess(&rsd->SavedICInfo[i]);
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:
RestoreActorProcess(rsd->SavedICInfo[i].actorid, &rsd->SavedICInfo[i]);
if (TinselV2)
RestoreProcess(&rsd->SavedICInfo[i]);
else
RestoreActorProcess(rsd->SavedICInfo[i].idActor, &rsd->SavedICInfo[i]);
break;
case GS_POLYGON:
case GS_SCENE:
RestoreProcess(&rsd->SavedICInfo[i]);
break;
default:
warning("Unhandled GSort in ResumeInterprets");
}
}
}
@ -219,7 +301,7 @@ void ResumeInterprets(SAVED_DATA *rsd) {
* Do restore scene
* @param n Id
*/
static int DoRestoreScene(SAVED_DATA *rsd, int n) {
static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
switch (n) {
case RS_COUNT + COUNTOUT_COUNT:
// Trigger pre-load and fade and start countdown
@ -229,31 +311,90 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
case RS_COUNT:
_vm->_sound->stopAllSamples();
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);
bNoFade = false;
startupBackground(rsd->SavedBgroundHandle);
KillScroll();
PlayfieldSetPos(FIELD_WORLD, rsd->SavedLoffset, rsd->SavedToffset);
bNoBlocking = rsd->SavedNoBlocking;
RestoreNoScrollData(&rsd->SavedNoScrollData);
/*
break;
StartupBackground(nullContext, sd->SavedBgroundHandle);
case RS_COUNT - 1:
*/
sortActors(rsd);
if (TinselV2) {
Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
} 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;
case 2:
break;
case 1:
RestoreMidiFacts(rsd->SavedMidi, rsd->SavedLoop);
if (rsd->SavedControl)
control(CONTROL_ON); // TOKEN_CONTROL was free
ResumeInterprets(rsd);
if (TinselV2) {
if (bNotDoneYet)
return n;
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;
@ -266,7 +407,7 @@ static int DoRestoreScene(SAVED_DATA *rsd, int n) {
void RestoreGame(int num) {
KillInventory();
RequestRestoreGame(num, &sgData, &savedSceneCount, s_ssData);
RequestRestoreGame(num, &sgData, &savedSceneCount, ssData);
// Actual restoring is performed by ProcessSRQueue
}
@ -278,9 +419,9 @@ void RestoreGame(int num) {
*/
void SaveGame(char *name, char *desc) {
// 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
}
@ -289,23 +430,23 @@ void SaveGame(char *name, char *desc) {
//---------------------------------------------------------------------------------
bool IsRestoringScene() {
if (s_restoreSceneCount) {
s_restoreSceneCount = DoRestoreScene(s_rsd, s_restoreSceneCount);
if (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
if (s_restoreSceneCount == 0) {
if (RestoreSceneCount == 0) {
assert(savedSceneCount >= 1); // No saved scene to restore
if (ASceneIsSaved)
RestoreScene(&s_ssData[--savedSceneCount], bFade);
DoRestoreScene(&ssData[--savedSceneCount], bFade);
if (!bFade)
bNoFade = true;
}
@ -314,7 +455,7 @@ void PleaseRestoreScene(bool bFade) {
/**
* Please Save Scene
*/
void PleaseSaveScene(CORO_PARAM) {
void TinselSaveScene(CORO_PARAM) {
// only called by save_scene PCODE
CORO_BEGIN_CONTEXT;
CORO_END_CONTEXT(_ctx);
@ -325,10 +466,10 @@ void PleaseSaveScene(CORO_PARAM) {
// Don't save the same thing multiple times!
// 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();
SaveScene(&s_ssData[savedSceneCount++]);
DoSaveScene(&ssData[savedSceneCount++]);
CORO_END_CODE;
}

View file

@ -33,13 +33,16 @@
#include "tinsel/dw.h" // SCNHANDLE
#include "tinsel/rince.h" // SAVED_MOVER
#include "tinsel/pcode.h" // INT_CONTEXT
#include "tinsel/play.h"
#include "tinsel/polygons.h"
#include "tinsel/scroll.h" // SCROLLDATA
#include "tinsel/sysvar.h"
namespace Tinsel {
enum {
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
// name field in savedFiles. Raising it to 256 as a preliminary fix.
@ -66,6 +69,16 @@ struct SAVED_DATA {
bool SavedLoop; // } Midi
bool SavedNoBlocking;
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
};
void PleaseRestoreScene(bool bFade);
void PleaseSaveScene(CORO_PARAM);
void TinselRestoreScene(bool bFade);
void TinselSaveScene(CORO_PARAM);
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut);
void DoSaveScene(SAVED_DATA *sd);
bool IsRestoringScene();
@ -95,8 +110,8 @@ void ProcessSRQueue(void);
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 InitialiseSs(void);
void FreeSs(void);
void InitialiseSaveScenes(void);
void FreeSaveScenes(void);
} // end of namespace Tinsel

View file

@ -29,21 +29,26 @@
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
#include "tinsel/dialogs.h"
#include "tinsel/dw.h"
#include "tinsel/graphics.h"
#include "tinsel/handle.h"
#include "tinsel/inventory.h"
#include "tinsel/film.h"
#include "tinsel/font.h"
#include "tinsel/mareels.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/sched.h"
#include "tinsel/scn.h"
#include "tinsel/scroll.h"
#include "tinsel/sound.h" // stopAllSamples()
#include "tinsel/object.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h" // process IDs
#include "tinsel/polygons.h"
#include "tinsel/sysvar.h"
#include "tinsel/token.h"
@ -72,20 +77,28 @@ extern void EnableTags(void);
/** scene structure - one per scene */
struct SCENE_STRUC {
int32 numEntrance; //!< number of entrances in this scene
int32 numPoly; //!< number of various polygons in this scene
int32 numActor; //!< number of actors in this scene
int32 defRefer; //!< Default refer direction
SCNHANDLE hSceneScript; //!< handle to scene script
SCNHANDLE hEntrance; //!< handle to table of entrances
SCNHANDLE hPoly; //!< handle to table of polygons
SCNHANDLE hActor; //!< handle to table of actors
int32 defRefer; // Default refer direction (REFTYPE)
SCNHANDLE hSceneScript; // handle to scene script
SCNHANDLE hSceneDesc; // handle to scene description
int32 numEntrance; // number of entrances in this scene
SCNHANDLE hEntrance; // handle to table of entrances
int32 numPoly; // number of various polygons in this scene
SCNHANDLE hPoly; // handle to table of polygons
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;
/** entrance structure - one per entrance */
struct ENTRANCE_STRUC {
int32 eNumber; //!< entrance number
SCNHANDLE hScript; //!< handle to entrance script
// Tinsel 2 fields
SCNHANDLE hEntDesc; // handle to entrance description
uint32 flags;
} PACKED_STRUCT;
#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
#endif
SCNHANDLE newestScene = 0;
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.
@ -107,20 +151,59 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
const TP_INIT *pInit;
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);
_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);
if (_ctx->pInit->event == CLOSEDOWN || _ctx->pInit->event == LEAVE_T2)
bWatchingOut = false;
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
* Initialise polygons for the scene
@ -128,47 +211,103 @@ static void SceneTinselProcess(CORO_PARAM, const void *param) {
* Run the appropriate entrance code (if any)
* Get the default refer type
*/
static void LoadScene(SCNHANDLE scene, int entry) {
uint i;
TP_INIT init;
const SCENE_STRUC *ss;
const ENTRANCE_STRUC *es;
uint i;
// Scene structure
// Scene handle
SceneHandle = scene; // Save scene handle in case of Save_Scene()
LockMem(SceneHandle); // Make sure scene is loaded
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);
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
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
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)
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 (es->hScript)
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &es->hScript, sizeof(es->hScript));
if (es->hScript) {
init.event = STARTUP;
init.hTinselCode = es->hScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
break;
}
// Move to next entrance
if (TinselV2)
++es;
else
es = (const ENTRANCE_STRUC *)((const byte *)es + 8);
}
if (i == FROM_LE_32(ss->numEntrance))
error("Non-existant scene entry number");
if (ss->hSceneScript)
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &ss->hSceneScript, sizeof(ss->hSceneScript));
if (ss->hSceneScript) {
init.event = STARTUP;
init.hTinselCode = ss->hSceneScript;
g_scheduler->createProcess(PID_TCODE, SceneTinselProcess, &init, sizeof(init));
}
}
// Default refer type
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
DropPolygons(); // No polygons
DropNoScrolls(); // No no-scrolls
DropScroll(); // No no-scrolls
DropBackground(); // No background
DropMActors(); // No moving actors
DropMovers(); // No moving actors
DropCursor(); // No cursor
DropActors(); // No actor reels running
FreeAllTokens(); // No-one has tokens
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->_mixer->stopAll();
// init the palette manager
ResetPalAllocator();
@ -251,10 +402,11 @@ void PrimeBackground(void) {
*/
void PrimeScene(void) {
bNoBlocking = false;
SetNoBlocking(false);
SetSysVar(SYS_SceneFxDimFactor, SysVar(SYS_DefaultFxDimFactor));
RestartCursor(); // Restart the cursor
if (!TinselV2)
EnableTags(); // Next scene with tags enabled
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.
*/
void NewScene(SCNHANDLE scene, int entry) {
void StartNewScene(SCNHANDLE scene, int entry) {
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.
LoadScene(scene, entry);
@ -303,4 +461,29 @@ SCNHANDLE GetSceneHandle(void) {
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

View file

@ -28,6 +28,7 @@
#define TINSEL_SCENE_H
#include "tinsel/dw.h"
#include "tinsel/events.h"
namespace Tinsel {
@ -39,13 +40,19 @@ enum {
MAX_ACTOR = 32 //!< maximum number of actors in a scene
};
// ENTRANCE_STRUC bitflags
enum ENTRANCE_FLAGS {
fCall = 0x00000001L,
fHook = 0x00000002L
};
/** reference direction */
enum REFTYPE {
REF_DEFAULT, REF_UP, REF_DOWN, REF_LEFT, REF_RIGHT, REF_POINT
};
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 */
@ -68,6 +75,23 @@ enum REEL {
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
#endif // TINSEL_SCENE_H

View file

@ -24,6 +24,10 @@
* Process scheduler.
*/
#include "tinsel/handle.h"
#include "tinsel/pcode.h"
#include "tinsel/pid.h"
#include "tinsel/polygons.h"
#include "tinsel/sched.h"
#include "common/util.h"
@ -32,18 +36,26 @@ namespace Tinsel {
Scheduler *g_scheduler = 0;
/** process structure */
struct PROCESS {
PROCESS *pNext; //!< pointer to next process in active or free list
#include "common/pack-start.h" // START STRUCT PACKING
CoroContext state; //!< the state of the coroutine
CORO_ADDR coroAddr; //!< the entry point of the coroutine
struct PROCESS_STRUC {
uint32 processId; // ID of process
SCNHANDLE hProcessCode; // handle to actor script
} PACKED_STRUCT;
int sleepTime; //!< number of scheduler cycles to sleep
int pid; //!< process ID
char param[PARAM_SIZE]; //!< process specific info
};
#include "common/pack-end.h" // END STRUCT PACKING
CoroContext nullContext = NULL;
//----------------- LOCAL GLOBAL DATA --------------------
static uint32 numSceneProcess;
static SCNHANDLE hSceneProcess;
static uint32 numGlobalProcess;
static PROCESS_STRUC *pGlobalProcess;
//--------------------- FUNCTIONS ------------------------
Scheduler::Scheduler() {
processList = 0;
@ -59,6 +71,7 @@ Scheduler::Scheduler() {
pRCfunction = 0;
active = new PROCESS;
active->pPrevious = NULL;
g_scheduler = this; // FIXME HACK
}
@ -83,7 +96,7 @@ void Scheduler::reset() {
if (processList == NULL) {
// 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
if (processList == NULL) {
@ -91,7 +104,7 @@ void Scheduler::reset() {
}
// fill with garbage
memset(processList, 'S', NUM_PROCESS * sizeof(PROCESS));
memset(processList, 'S', MAX_PROCESSES * sizeof(PROCESS));
}
// no active processes
@ -101,12 +114,10 @@ void Scheduler::reset() {
pFreeProcesses = processList;
// link all other processes after first
for (int i = 1; i < NUM_PROCESS; i++) {
processList[i - 1].pNext = processList + i;
for (int i = 1; i <= NUM_PROCESS; 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
#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
*/
void Scheduler::schedule(void) {
// start dispatching active process list
PROCESS *pPrevProc = active;
PROCESS *pNext;
PROCESS *pProc = active->pNext;
while (pProc != NULL) {
pNext = pProc->pNext;
if (--pProc->sleepTime <= 0) {
// process is ready for dispatch, activate it
pCurrent = pProc;
pProc->coroAddr(pProc->state, pProc->param);
pCurrent = NULL;
if (!pProc->state || pProc->state->_sleep <= 0) {
// Coroutine finished
pCurrent = pCurrent->pPrevious;
killProcess(pProc);
pProc = pPrevProc;
} else {
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.
@ -172,16 +322,27 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
// get link to next free process
pFreeProcesses = pProc->pNext;
if (pFreeProcesses)
pFreeProcesses->pPrevious = NULL;
if (pCurrent != NULL) {
// place new process before the next active process
pProc->pNext = pCurrent->pNext;
if (pProc->pNext)
pProc->pNext->pPrevious = pProc;
// make this new process the next active process
pCurrent->pNext = pProc;
pProc->pPrevious = pCurrent;
} else { // no active processes, place process at head of list
pProc->pNext = active->pNext;
pProc->pPrevious = active;
if (pProc->pNext)
pProc->pNext->pPrevious = pProc;
active->pNext = pProc;
}
// set coroutine entry point
@ -204,7 +365,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
memcpy(pProc->param, pParam, sizeParam);
}
// return created process
return pProc;
}
@ -215,8 +375,6 @@ PROCESS *Scheduler::createProcess(int pid, CORO_ADDR coroAddr, const void *pPara
* @param pKillProc which process to kill
*/
void Scheduler::killProcess(PROCESS *pKillProc) {
PROCESS *pProc, *pPrev; // process list pointers
// make sure a valid process pointer
assert(pKillProc >= processList && pKillProc <= processList + NUM_PROCESS - 1);
@ -229,32 +387,25 @@ void Scheduler::killProcess(PROCESS *pKillProc) {
assert(numProcs >= 0);
#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
if (pRCfunction != NULL)
(pRCfunction)(pProc);
(pRCfunction)(pKillProc);
delete pProc->state;
delete pKillProc->state;
// make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext;
// Take the process out of the active chain list
pKillProc->pPrevious->pNext = pKillProc->pNext;
if (pKillProc->pNext)
pKillProc->pNext->pPrevious = pKillProc->pPrevious;
// 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
pFreeProcesses = pProc;
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");
// make pKillProc the first free process
pFreeProcesses = pKillProc;
}
@ -302,11 +453,21 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
// kill this process
numKilled++;
// Free the process' resources
if (pRCfunction != NULL)
(pRCfunction)(pProc);
delete pProc->state;
// make prev point to next to unlink pProc
pPrev->pNext = pProc->pNext;
if (pProc->pNext)
pPrev->pNext->pPrevious = pPrev;
// link first free process after pProc
pProc->pNext = pFreeProcesses;
pProc->pPrevious = NULL;
pFreeProcesses->pPrevious = pProc;
// make pProc the first free process
pFreeProcesses = pProc;
@ -327,8 +488,6 @@ int Scheduler::killMatchingProcess(int pidKill, int pidMask) {
return numKilled;
}
/**
* Set pointer to a function to be called by killProcess().
*
@ -342,4 +501,262 @@ void Scheduler::setResourceCallback(VFPTRPP 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

View file

@ -29,6 +29,8 @@
#include "tinsel/dw.h" // new data types
#include "tinsel/coroutine.h"
#include "tinsel/events.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
@ -36,12 +38,26 @@ namespace Tinsel {
#define PARAM_SIZE 32
// 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 *);
/** 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).
@ -69,6 +85,8 @@ private:
// diagnostic process counters
int numProcs;
int maxProcs;
void CheckStack();
#endif
/**
@ -90,13 +108,16 @@ public:
#endif
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);
void killProcess(PROCESS *pKillProc);
PROCESS *getCurrentProcess();
int getCurrentPID() const;
int killMatchingProcess(int pidKill, int pidMask);
int killMatchingProcess(int pidKill, int pidMask = -1);
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
//----------------- 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
#endif // TINSEL_SCHED_H

View file

@ -47,10 +47,15 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
uint32 *lptr = (uint32 *)bptr;
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
// the same in V1 and V2
if (_vm->getVersion() == TINSEL_V0 &&
// the same in V0 and V1
if (TinselVersion == TINSEL_V0 &&
chunk != CHUNK_STRING && chunk != CHUNK_BITMAP)
chunk -= 0x2L;
@ -70,10 +75,10 @@ byte *FindChunk(SCNHANDLE handle, uint32 chunk) {
/**
* Get the actor id from a film (column 0)
*/
int extractActor(SCNHANDLE film) {
const FILM *pfilm = (const FILM *)LockMem(film);
const FREEL *preel = &pfilm->reels[0];
const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(preel->mobj));
int ExtractActor(SCNHANDLE hFilm) {
const FILM *pFilm = (const FILM *)LockMem(hFilm);
const FREEL *pReel = &pFilm->reels[0];
const MULTI_INIT *pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj));
return (int)FROM_LE_32(pmi->mulID);
}

View file

@ -30,6 +30,14 @@
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
@ -48,20 +56,42 @@ namespace Tinsel {
#define CHUNK_ENTRANCE 0x3334000BL // not used!
#define CHUNK_POLYGONS 0x3334000CL // not used!
#define CHUNK_ACTORS 0x3334000DL // not used!
#define CHUNK_SCENE 0x3334000EL
#define CHUNK_TOTAL_ACTORS 0x3334000FL
#define CHUNK_TOTAL_GLOBALS 0x33340010L
#define CHUNK_TOTAL_OBJECTS 0x33340011L
#define CHUNK_OBJECTS 0x33340012L
#define CHUNK_MIDI 0x33340013L // not used!
#define CHUNK_SAMPLE 0x33340014L // not used!
#define CHUNK_TOTAL_POLY 0x33340015L
#define CHUNK_MBSTRING 0x33340022L // Multi-byte characters
#define CHUNK_PROCESSES 0x3334000EL // Tinsel 2 only
// Following chunk Ids should be decremented by 1 for Tinsel 1
#define CHUNK_SCENE 0x3334000FL
#define CHUNK_TOTAL_ACTORS 0x33340010L
#define CHUNK_TOTAL_GLOBALS 0x33340011L
#define CHUNK_TOTAL_OBJECTS 0x33340012L
#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
byte *FindChunk(SCNHANDLE handle, uint32 chunk);
int extractActor(SCNHANDLE film);
int ExtractActor(SCNHANDLE hFilm);
} // end of namespace Tinsel

View file

@ -33,17 +33,11 @@
#include "tinsel/rince.h"
#include "tinsel/scroll.h"
#include "tinsel/sched.h"
#include "tinsel/sysvar.h"
#include "tinsel/tinsel.h"
namespace Tinsel {
//----------------- EXTERNAL FUNCTIONS ---------------------
// in BG.C
extern int BackgroundWidth(void);
extern int BackgroundHeight(void);
//----------------- LOCAL DEFINES --------------------
#define LEFT 'L'
@ -58,7 +52,7 @@ extern int BackgroundHeight(void);
static int LeftScroll = 0, DownScroll = 0; // Number of iterations outstanding
static int scrollActor = 0;
static PMACTOR psActor = 0;
static PMOVER pScrollMover = 0;
static int oldx = 0, oldy = 0;
/** 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,
// DW2 fields
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
// 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.
* Checks for no-scroll boundaries and sets off a scroll if allowed.
@ -157,9 +153,14 @@ static void NeedScroll(int direction) {
}
if (LeftScroll <= 0) {
scrollPixels = SCROLLPIXELS;
if (TinselV2) {
scrollPixelsX = sd.xSpeed;
LeftScroll += sd.xDistance;
} else {
scrollPixelsX = SCROLLPIXELS;
LeftScroll = RLSCROLL;
}
}
break;
case RIGHT: /* Picture will go right, 'camera' left */
@ -175,9 +176,14 @@ static void NeedScroll(int direction) {
}
if (LeftScroll >= 0) {
scrollPixels = SCROLLPIXELS;
if (TinselV2) {
scrollPixelsX = sd.xSpeed;
LeftScroll -= sd.xDistance;
} else {
scrollPixelsX = SCROLLPIXELS;
LeftScroll = -RLSCROLL;
}
}
break;
case UP: /* Picture will go upwards, 'camera' downwards */
@ -194,9 +200,14 @@ static void NeedScroll(int direction) {
}
if (DownScroll <= 0) {
scrollPixels = SCROLLPIXELS;
if (TinselV2) {
scrollPixelsY = sd.ySpeed;
DownScroll += sd.yDistance;
} else {
scrollPixelsY = SCROLLPIXELS;
DownScroll = UDSCROLL;
}
}
break;
case DOWN: /* Picture will go downwards, 'camera' upwards */
@ -212,9 +223,14 @@ static void NeedScroll(int direction) {
}
if (DownScroll >= 0) {
scrollPixels = SCROLLPIXELS;
if (TinselV2) {
scrollPixelsY = sd.ySpeed;
DownScroll -= sd.yDistance;
} else {
scrollPixelsY = SCROLLPIXELS;
DownScroll = -UDSCROLL;
}
}
break;
}
}
@ -234,7 +250,7 @@ static void ScrollImage(void) {
* Keeping cursor on a tag?
*/
if (ScrollCursor) {
GetCursorXY(&curX, &curY, true);
GetCursorXYNoWait(&curX, &curY, true);
if (InPolygon(curX, curY, TAG) != NOPOLY || InPolygon(curX, curY, EXIT) != NOPOLY) {
OldLoffset = Loffset;
OldToffset = Toffset;
@ -246,49 +262,66 @@ static void ScrollImage(void) {
* Horizontal scrolling
*/
if (LeftScroll > 0) {
LeftScroll -= scrollPixels;
LeftScroll -= scrollPixelsX;
if (LeftScroll < 0) {
Loffset += LeftScroll;
LeftScroll = 0;
}
Loffset += scrollPixels; // Move right
Loffset += scrollPixelsX; // Move right
if (Loffset > ImageW - SCREEN_WIDTH)
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) {
LeftScroll += scrollPixels;
LeftScroll += scrollPixelsX;
if (LeftScroll > 0) {
Loffset += LeftScroll;
LeftScroll = 0;
}
Loffset -= scrollPixels; // Move left
Loffset -= scrollPixelsX; // Move left
if (Loffset < 0)
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
*/
if (DownScroll > 0) {
DownScroll -= scrollPixels;
DownScroll -= scrollPixelsY;
if (DownScroll < 0) {
Toffset += DownScroll;
DownScroll = 0;
}
Toffset += scrollPixels; // Move down
Toffset += scrollPixelsY; // Move down
if (Toffset > ImageH - SCREEN_HEIGHT)
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) {
DownScroll += scrollPixels;
DownScroll += scrollPixelsY;
if (DownScroll > 0) {
Toffset += DownScroll;
DownScroll = 0;
}
Toffset -= scrollPixels; // Move up
Toffset -= scrollPixelsY; // Move up
if (Toffset < 0)
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
*/
if (!psActor || getMActorHideState(psActor)
|| getMActorState(psActor) == NO_MACTOR)
if (!pScrollMover || MoverHidden(pScrollMover) || !MoverIs(pScrollMover))
return;
GetActorPos(scrollActor, &newx, &newy);
@ -326,7 +358,7 @@ static void MonitorScroll(void) {
/*
* 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)
NeedScroll(LEFT);
} else if (newx < Loffset + RLDISTANCE && Loffset) {
@ -337,10 +369,10 @@ static void MonitorScroll(void) {
/*
* 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)
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)
NeedScroll(DOWN);
}
@ -349,6 +381,30 @@ static void MonitorScroll(void) {
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.
*/
@ -359,21 +415,28 @@ void ScrollProcess(CORO_PARAM, const void *) {
CORO_BEGIN_CODE(_ctx);
ImageH = BackgroundHeight(); // Dimensions
ImageW = BackgroundWidth(); // of this scene.
// In Tinsel v2, scenes may play movies, so the background may not always
// 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
if (ImageW == SCREEN_WIDTH && ImageH == SCREEN_HEIGHT)
CORO_KILL_SELF();
if (!TinselV2) {
LeftScroll = DownScroll = 0; // No iterations outstanding
oldx = oldy = 0;
scrollPixels = SCROLLPIXELS;
scrollPixelsX = scrollPixelsY = SCROLLPIXELS;
}
if (!scrollActor)
scrollActor = LeadId();
scrollActor = GetLeadId();
psActor = GetMover(scrollActor);
pScrollMover = GetMover(scrollActor);
while (1) {
MonitorScroll(); // Set scroll requirement
@ -395,17 +458,26 @@ void ScrollFocus(int ano) {
oldx = oldy = 0;
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.
*/
void ScrollTo(int x, int y, int iter) {
void ScrollTo(int x, int y, int xIter, int yIter) {
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
@ -429,4 +501,35 @@ void RestoreNoScrollData(SCROLLDATA *ssd) {
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

View file

@ -30,8 +30,10 @@ namespace Tinsel {
#define SCROLLPIXELS 8 // Number of pixels to scroll per iteration
#define RLDISTANCE 50 // Distance from edge that triggers a scroll
#define UDDISTANCE 20
// Distance from edge that triggers a scroll
#define RLDISTANCE (TinselV2 ? sd.xTrigger : 50)
#define UDISTANCE (TinselV2 ? sd.yTriggerTop : 20)
#define DDISTANCE (TinselV2 ? sd.yTriggerBottom : 20)
// Number of iterations to make
#define RLSCROLL 160 // 20*8 = 160 = half a screen
@ -52,26 +54,39 @@ struct SCROLLDATA{
NOSCROLLB NoVScroll[MAX_VNOSCROLL]; // Vertical no-scroll boundaries
NOSCROLLB NoHScroll[MAX_HNOSCROLL]; // Horizontal 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 DoScrollCursor(void);
void SetNoScroll(int x1, int y1, int x2, int y2);
void DropNoScrolls(void);
void DropScroll(void);
void ScrollProcess(CORO_PARAM, const void *);
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 GetNoScrollData(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
#endif /* TINSEL_SCROLL_H */

View file

@ -31,6 +31,8 @@
#include "tinsel/music.h"
#include "tinsel/strres.h"
#include "tinsel/tinsel.h"
#include "tinsel/sysvar.h"
#include "tinsel/background.h"
#include "common/endian.h"
#include "common/file.h"
@ -38,14 +40,20 @@
#include "sound/mixer.h"
#include "sound/audiocd.h"
#include "sound/adpcm.h"
namespace Tinsel {
extern LANGUAGE sampleLanguage;
//--------------------------- General data ----------------------------------
SoundManager::SoundManager(TinselEngine *vm) :
//_vm(vm), // TODO: Enable this once global _vm var is gone
_sampleIndex(0), _sampleIndexLen(0) {
for (int i = 0; i < kNumChannels; i++)
_channels[i].sampleNum = _channels[i].subSample = -1;
}
SoundManager::~SoundManager() {
@ -67,24 +75,29 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
if (!_vm->_mixer->isReady())
return false;
Channel &curChan = _channels[kChannelTinsel1];
// stop any currently playing sample
_vm->_mixer->stopHandle(_handle);
_vm->_mixer->stopHandle(curChan.handle);
// make sure id is in range
assert(id > 0 && id < _sampleIndexLen);
curChan.sampleNum = id;
curChan.subSample = 0;
// get file offset for this sample
int32 dwSampleIndex = _sampleIndex[id];
uint32 dwSampleIndex = _sampleIndex[id];
// move to correct position in the sample file
_sampleStream.seek(dwSampleIndex);
if (_sampleStream.ioFailed() || _sampleStream.pos() != dwSampleIndex)
error("File %s is corrupt", SAMPLE_FILE);
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 %s is corrupt", SAMPLE_FILE);
error(FILE_IS_CORRUPT, _vm->getSampleFile(sampleLanguage));
// allocate a buffer
void *sampleBuf = malloc(sampleLen);
@ -92,7 +105,7 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// read all of the sample
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 ;)
_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volSound);
@ -101,15 +114,211 @@ bool SoundManager::playSample(int id, Audio::Mixer::SoundType type, Audio::Sound
// 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);
if (handle)
*handle = _handle;
*handle = curChan.handle;
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.
* @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.
*/
bool SoundManager::sampleIsPlaying(void) {
return _vm->_mixer->isSoundHandleActive(_handle);
bool SoundManager::sampleIsPlaying(int id) {
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) {
// 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;
// open sample index file in binary mode
if (f.open(SAMPLE_INDEX)) {
if (f.open(_vm->getSampleIndex(sampleLanguage))) {
// get length of index file
f.seek(0, SEEK_END); // move to end of file
_sampleIndexLen = f.pos(); // get file pointer
@ -166,7 +413,7 @@ void SoundManager::openSampleFiles(void) {
if (_sampleIndex == NULL) {
// allocate a buffer for the indices
_sampleIndex = (int32 *)malloc(_sampleIndexLen);
_sampleIndex = (uint32 *)malloc(_sampleIndexLen);
// make sure memory allocated
if (_sampleIndex == NULL) {
@ -179,7 +426,7 @@ void SoundManager::openSampleFiles(void) {
// load data
if (f.read(_sampleIndex, _sampleIndexLen) != (uint32)_sampleIndexLen)
// 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
// Convert all ids from LE to native format
@ -194,18 +441,25 @@ void SoundManager::openSampleFiles(void) {
// convert file size to size in DWORDs
_sampleIndexLen /= sizeof(uint32);
} else
error("Cannot find file %s", SAMPLE_INDEX);
error(CANNOT_FIND_FILE, _vm->getSampleIndex(sampleLanguage));
// open sample file in binary mode
if (!_sampleStream.open(SAMPLE_FILE))
error("Cannot find file %s", SAMPLE_FILE);
if (!_sampleStream.open(_vm->getSampleFile(sampleLanguage)))
error(CANNOT_FIND_FILE, _vm->getSampleFile(sampleLanguage));
/*
// gen length of the largest sample
sampleBuffer.size = _sampleStream.readUint32LE();
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

View file

@ -37,6 +37,11 @@
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 *|
@ -44,14 +49,39 @@ namespace Tinsel {
class SoundManager {
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
/** Sample handle */
Audio::SoundHandle _handle;
/** Sample index buffer and number of entries */
int32 *_sampleIndex;
uint32 *_sampleIndex;
/** Number of entries in the sample index */
long _sampleIndexLen;
@ -59,19 +89,29 @@ protected:
/** file stream for sample file */
Common::File _sampleStream;
bool offscreenChecks(int x, int &y);
int8 getPan(int x);
public:
SoundManager(TinselEngine *vm);
~SoundManager();
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 stopSpecSample(int id, int sub = 0); // Stops a specific sample
void setSFXVolumes(uint8 volume);
bool sampleExists(int id);
bool sampleIsPlaying(void);
bool sampleIsPlaying(int id = -1);
// TODO: Internal method, make this protected?
void openSampleFiles(void);
void closeSampleStream(void);
};
} // end of namespace Tinsel

View file

@ -40,18 +40,39 @@ int newestString;
// buffer for resource strings
static uint8 *textBuffer = 0;
// language resource string filenames
static const char *languageFiles[] = {
"english.txt",
"french.txt",
"german.txt",
"italian.txt",
"spanish.txt"
static struct {
bool bPresent;
const char *szStem;
SCNHANDLE hDescription;
SCNHANDLE hFlagFilm;
} 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.
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
* @param newLang The new language
@ -60,6 +81,9 @@ void ChangeLanguage(LANGUAGE newLang) {
Common::File f;
uint32 textLen = 0; // length of buffer
textLanguage = newLang;
sampleLanguage = newLang;
if (textBuffer) {
// free the previous buffer
free(textBuffer);
@ -69,9 +93,9 @@ void ChangeLanguage(LANGUAGE newLang) {
// 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
// language versions reused it rather than their proper filename
if (!f.open(languageFiles[newLang])) {
if ((newLang == TXT_ENGLISH) || !f.open(languageFiles[TXT_ENGLISH]))
error("Cannot find file %s", languageFiles[newLang]);
if (!f.open(_vm->getTextFile(newLang))) {
if ((newLang == TXT_ENGLISH) || !f.open(_vm->getTextFile(TXT_ENGLISH)))
error(CANNOT_FIND_FILE, _vm->getTextFile(newLang));
}
// Check whether the file is compressed or not - for compressed files the
@ -79,7 +103,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// identifier
textLen = f.readUint32LE();
if (f.ioFailed())
error("File %s is corrupt", languageFiles[newLang]);
error(FILE_IS_CORRUPT, _vm->getTextFile(newLang));
if (textLen == CHUNK_STRING || textLen == CHUNK_MBSTRING) {
// the file is uncompressed
@ -101,7 +125,7 @@ void ChangeLanguage(LANGUAGE newLang) {
// load data
if (f.read(textBuffer, textLen) != textLen)
// 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
f.close();
@ -111,19 +135,11 @@ void ChangeLanguage(LANGUAGE newLang) {
}
/**
* 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
* FindStringBase
*/
int LoadStringRes(int id, char *pBuffer, int bufferMax) {
#ifdef DEBUG
// For diagnostics
newestString = id;
#endif
static byte *FindStringBase(int id) {
// base of string resource table
uint8 *pText = textBuffer;
byte *pText = textBuffer;
// index into text resource file
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
int strSkip = id % STRINGS_PER_CHUNK;
// length of string
int len;
// skip to the correct chunk
while (chunkSkip-- != 0) {
// make sure chunk id is correct
assert(READ_LE_UINT32(pText + index) == CHUNK_STRING || READ_LE_UINT32(pText + index) == CHUNK_MBSTRING);
if (READ_LE_UINT32(pText + index + sizeof(uint32)) == 0) {
// TEMPORARY DIRTY BODGE
strcpy(pBuffer, "!! HIGH STRING !!");
// string does not exist
return 0;
return NULL;
}
// get index to next chunk
@ -163,17 +173,114 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
// skip to the correct string
while (strSkip-- != 0) {
// 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;
}
}
}
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
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
// copy the string to the buffer
if (len < bufferMax) {
if (len < bufferMax)
{
memcpy(pBuffer, pText + 1, len);
// null terminate
@ -199,6 +306,42 @@ int LoadStringRes(int id, char *pBuffer, int bufferMax) {
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() {
if (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

View file

@ -58,11 +58,44 @@ void ChangeLanguage(LANGUAGE newLang);
*/
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()
*/
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
#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 hFont Which font to use
* @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,
SCNHANDLE hFont, int mode) {
OBJECT *ObjectTextOut(CORO_PARAM, OBJECT *pList, char *szStr, int colour,
int xPos, int yPos, SCNHANDLE hFont, int mode, int sleepTime) {
int xJustify; // x position of text after justification
int yOffset; // offset to next line of text
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']));
// 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) {
// 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
pChar->hImg = hImg; // image def
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
// check for absolute positioning

View file

@ -27,6 +27,7 @@
#ifndef TINSEL_TEXT_H // prevent multiple includes
#define TINSEL_TEXT_H
#include "tinsel/coroutine.h"
#include "tinsel/object.h" // object manager defines
namespace Tinsel {
@ -42,6 +43,10 @@ enum {
/** maximum number of characters in a font */
#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
@ -80,14 +85,21 @@ struct TEXTOUT {
|* Text Function Prototypes *|
\*----------------------------------------------------------------------*/
OBJECT *ObjectTextOut( // output a string of text
OBJECT *pList, // object list to add text to
char *szStr, // string to output
int colour, // colour for monochrome text
int xPos, // x position of string
int yPos, // y position of string
SCNHANDLE hFont, // which font to use
int mode); // mode flags for the string
/**
* Main text outputting routine. If a object list is specified a
* multi-object is created for the whole text and a pointer to the head
* of the list is returned.
* @param pList object list to add text to
* @param szStr string to output
* @param colour colour for monochrome text
* @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
TEXTOUT *pText); // pointer to TextOut struct with all parameters

View file

@ -151,7 +151,7 @@ void FettleTimers(void) {
/**
* 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;
assert(num); // zero is not allowed as a timer number

View file

@ -44,7 +44,7 @@ void syncTimerInfo(Serializer &s);
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);

File diff suppressed because it is too large Load diff

View file

@ -28,13 +28,40 @@
#define TINSEL_TINLIB_H
#include "tinsel/dw.h"
#include "tinsel/object.h"
#include "tinsel/palette.h"
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
void control(int param);
void stand(int actor, int x, int y, SCNHANDLE film);
void ActorBrightness(int actor, int brightness);
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

View file

@ -24,6 +24,7 @@
*/
#include "common/endian.h"
#include "common/error.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/file.h"
@ -44,13 +45,15 @@
#include "tinsel/background.h"
#include "tinsel/config.h"
#include "tinsel/cursor.h"
#include "tinsel/drives.h"
#include "tinsel/dw.h"
#include "tinsel/events.h"
#include "tinsel/faders.h"
#include "tinsel/film.h"
#include "tinsel/handle.h"
#include "tinsel/heapmem.h" // MemoryInit
#include "tinsel/inventory.h"
#include "tinsel/dialogs.h"
#include "tinsel/mareels.h"
#include "tinsel/music.h"
#include "tinsel/object.h"
#include "tinsel/pid.h"
@ -60,6 +63,7 @@
#include "tinsel/serializer.h"
#include "tinsel/sound.h"
#include "tinsel/strres.h"
#include "tinsel/sysvar.h"
#include "tinsel/timers.h"
#include "tinsel/tinsel.h"
@ -71,6 +75,14 @@ namespace Tinsel {
extern void SetDoFadeIn(bool tf);
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
extern void CursorProcess(CORO_PARAM, const void *);
@ -79,7 +91,6 @@ extern void InventoryProcess(CORO_PARAM, const void *);
// In SCENE.CPP
extern void PrimeBackground();
extern void NewScene(SCNHANDLE scene, int entry);
extern SCNHANDLE GetSceneHandle(void);
// In TIMER.CPP
@ -94,6 +105,12 @@ void SetNewScene(SCNHANDLE scene, int entrance, int transition);
bool bRestart = false;
bool bHasRestarted = false;
static bool bCuttingScene = false;
static bool bChangingForRestore = false;
static Common::Point clickPos;
#ifdef DEBUG
bool bFast; // set to make it go ludicrously fast
#endif
@ -110,11 +127,11 @@ static Scene NextScene = { 0, 0, 0 };
static Scene HookScene = { 0, 0, 0 };
static Scene DelayedScene = { 0, 0, 0 };
static bool bHookSuspend = false;
static PROCESS *pMouseProcess = 0;
static PROCESS *pKeyboardProcess = 0;
static SCNHANDLE hCdChangeScene;
// Stack of pending mouse button events
Common::List<Common::EventType> mouseButtons;
@ -142,6 +159,7 @@ void KeyboardProcess(CORO_PARAM, const void *) {
// Get the next keyboard event off the stack
Common::Event evt = *keypresses.begin();
keypresses.erase(keypresses.begin());
const Common::Point mousePos = _vm->getMousePosition();
// Switch for special keys
switch (evt.kbd.keycode) {
@ -150,21 +168,21 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_RALT:
if (evt.type == Common::EVENT_KEYDOWN) {
if (!bSwapButtons)
ProcessButEvent(BE_RDSTART);
ProcessButEvent(PLR_DRAG2_START);
else
ProcessButEvent(BE_LDSTART);
ProcessButEvent(PLR_DRAG1_START);
} else {
if (!bSwapButtons)
ProcessButEvent(BE_LDEND);
ProcessButEvent(PLR_DRAG1_END);
else
ProcessButEvent(BE_RDEND);
ProcessButEvent(PLR_DRAG2_END);
}
continue;
case Common::KEYCODE_LCTRL:
case Common::KEYCODE_RCTRL:
if (evt.type == Common::EVENT_KEYDOWN) {
ProcessKeyEvent(LOOK_KEY);
ProcessKeyEvent(PLR_LOOK);
} else {
// Control key release
}
@ -186,42 +204,42 @@ void KeyboardProcess(CORO_PARAM, const void *) {
switch (evt.kbd.keycode) {
/*** SPACE = WALKTO ***/
case Common::KEYCODE_SPACE:
ProcessKeyEvent(WALKTO_KEY);
ProcessKeyEvent(PLR_WALKTO);
continue;
/*** RETURN = ACTION ***/
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
ProcessKeyEvent(ACTION_KEY);
ProcessKeyEvent(PLR_ACTION);
continue;
/*** l = LOOK ***/
case Common::KEYCODE_l: // LOOK
ProcessKeyEvent(LOOK_KEY);
ProcessKeyEvent(PLR_LOOK);
continue;
case Common::KEYCODE_ESCAPE:
// WORKAROUND: Check if any of the starting logo screens are active, and if so
// manually skip to the title screen, allowing them to be bypassed
{
if (!TinselV2) {
// 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 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) &&
((sceneNumber >= 25 && sceneNumber <= 27) || (sceneNumber == 17))) {
// Skip to title screen
// 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
SetNewScene((11 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
} else
#endif
if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
} else if ((sceneNumber >= 13) && (sceneNumber <= 15) || (sceneNumber == 11)) {
// Skip to title screen
SetNewScene((10 + sceneOffset) << SCNHANDLE_SHIFT, 1, TRANS_CUT);
} else {
// 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;
@ -236,48 +254,79 @@ void KeyboardProcess(CORO_PARAM, const void *) {
case Common::KEYCODE_F1:
// Options dialog
ProcessKeyEvent(OPTION_KEY);
ProcessKeyEvent(PLR_MENU);
continue;
case Common::KEYCODE_F5:
// Save game
ProcessKeyEvent(SAVE_KEY);
ProcessKeyEvent(PLR_SAVE);
continue;
case Common::KEYCODE_F7:
// Load game
ProcessKeyEvent(LOAD_KEY);
ProcessKeyEvent(PLR_LOAD);
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:
if ((evt.kbd.flags == Common::KBD_CTRL) || (evt.kbd.flags == Common::KBD_ALT))
ProcessKeyEvent(QUIT_KEY);
ProcessKeyEvent(PLR_QUIT);
continue;
case Common::KEYCODE_PAGEUP:
case Common::KEYCODE_KP9:
ProcessKeyEvent(PGUP_KEY);
ProcessKeyEvent(PLR_PGUP);
continue;
case Common::KEYCODE_PAGEDOWN:
case Common::KEYCODE_KP3:
ProcessKeyEvent(PGDN_KEY);
ProcessKeyEvent(PLR_PGDN);
continue;
case Common::KEYCODE_HOME:
case Common::KEYCODE_KP7:
ProcessKeyEvent(HOME_KEY);
ProcessKeyEvent(PLR_HOME);
continue;
case Common::KEYCODE_END:
case Common::KEYCODE_KP1:
ProcessKeyEvent(END_KEY);
ProcessKeyEvent(PLR_END);
continue;
default:
ProcessKeyEvent(NOEVENT_KEY);
ProcessKeyEvent(PLR_NOEVENT);
break;
}
}
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.
*/
void MouseProcess(CORO_PARAM, const void *) {
static void MouseProcess(CORO_PARAM, const void *) {
// COROUTINE
CORO_BEGIN_CONTEXT;
bool lastLWasDouble;
@ -292,9 +341,6 @@ void MouseProcess(CORO_PARAM, const void *) {
_ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime();
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()) {
// allow scheduling
@ -306,23 +352,46 @@ void MouseProcess(CORO_PARAM, const void *) {
Common::EventType type = *mouseButtons.begin();
mouseButtons.erase(mouseButtons.begin());
int xp, yp;
GetCursorXYNoWait(&xp, &yp, true);
const Common::Point mousePos(xp, yp);
switch (type) {
case Common::EVENT_LBUTTONDOWN:
// left button press
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
ProcessButEvent(BE_LDSTART);
ProcessButEvent(PLR_DRAG1_START);
// signal left double click event
ProcessButEvent(BE_DLEFT);
ProcessButEvent(PLR_DLEFT);
}
_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 {
// signal left drag start
ProcessButEvent(BE_LDSTART);
ProcessButEvent(PLR_DRAG1_START);
// signal left single click event
ProcessButEvent(BE_SLEFT);
ProcessButEvent(PLR_SLEFT);
}
_ctx->lastLWasDouble = false;
}
@ -332,32 +401,53 @@ void MouseProcess(CORO_PARAM, const void *) {
// left button release
// update click timer
if (_ctx->lastLWasDouble == false)
if (_ctx->lastLWasDouble == false) {
_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;
if (TinselV2)
// Signal left drag end
PlayerEvent(PLR_DRAG1_END, mousePos);
else
// signal left drag end
ProcessButEvent(BE_LDEND);
ProcessButEvent(PLR_DRAG1_END);
break;
case Common::EVENT_RBUTTONDOWN:
// right button press
if (DwGetCurrentTime() - _ctx->lastRightClick < (uint32)dclickSpeed) {
// Right button double-click
if (TinselV2) {
PlayerEvent(PLR_NOEVENT, clickPos);
} else {
// signal right drag start
ProcessButEvent(BE_RDSTART);
ProcessButEvent(PLR_DRAG2_START);
// signal right double click event
ProcessButEvent(BE_DRIGHT);
ProcessButEvent(PLR_DRIGHT);
}
_ctx->lastRWasDouble = true;
} else {
if (TinselV2) {
PlayerEvent(PLR_DRAG2_START, mousePos);
PlayerEvent(PLR_LOOK, mousePos);
} else {
// signal right drag start
ProcessButEvent(BE_RDSTART);
ProcessButEvent(PLR_DRAG2_START);
// signal right single click event
ProcessButEvent(BE_SRIGHT);
ProcessButEvent(PLR_SRIGHT);
}
_ctx->lastRWasDouble = false;
}
@ -372,8 +462,12 @@ void MouseProcess(CORO_PARAM, const void *) {
else
_ctx->lastRightClick -= dclickSpeed;
if (TinselV2)
// Signal left drag end
PlayerEvent(PLR_DRAG2_END, mousePos);
else
// signal right drag end
ProcessButEvent(BE_RDEND);
ProcessButEvent(PLR_DRAG2_END);
break;
default:
@ -402,9 +496,24 @@ static void MasterScriptProcess(CORO_PARAM, const void *) {
/**
* Store the facts pertaining to a scene change.
*/
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
NextScene.scene = scene;
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) {
assert(HookScene.scene == 0); // scene already hooked
@ -432,6 +544,9 @@ void SetHookScene(SCNHANDLE scene, int entrance, int transition) {
HookScene.trans = transition;
}
/**
* Hooked scene is over, trigger a change to the delayed scene
*/
void UnHookScene(void) {
assert(DelayedScene.scene != 0); // no scene delayed
@ -444,11 +559,40 @@ void UnHookScene(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) {
bHookSuspend = false;
bCuttingScene = false;
}
void syncSCdata(Serializer &s) {
@ -468,16 +612,23 @@ static void RestoredProcess(CORO_PARAM, const void *param) {
// COROUTINE
CORO_BEGIN_CONTEXT;
INT_CONTEXT *pic;
bool bConverse;
CORO_END_CONTEXT(_ctx);
CORO_BEGIN_CODE(_ctx);
// 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->bConverse = TinselV2 && (_ctx->pic->event == CONVERSE);
CORO_INVOKE_1(Interpret, _ctx->pic);
// Restore control after CallScene() from a conversation icon
if (_ctx->bConverse)
ControlOn();
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 |
* 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())
return;
return true;
if (NextScene.scene != 0) {
if (!CountOut) {
@ -517,12 +675,15 @@ void ChangeScene() {
// Trigger pre-load and fade and start countdown
CountOut = COUNTOUT_COUNT;
FadeOutFast(NULL);
if (TinselV2)
_vm->_pcmMusic->startFadeOut(COUNTOUT_COUNT);
break;
}
} else if (--CountOut == 0) {
if (!TinselV2)
ClearScreen();
NewScene(NextScene.scene, NextScene.entry);
StartNewScene(NextScene.scene, NextScene.entry);
NextScene.scene = 0;
switch (NextScene.trans) {
@ -535,20 +696,32 @@ void ChangeScene() {
SetDoFadeIn(true);
break;
}
} else
_vm->_pcmMusic->fadeOutIteration();
}
}
return false;
}
/**
* CuttingScene
*/
void CuttingScene(bool bCutting) {
bCuttingScene = bCutting;
if (!bCutting)
WrapScene();
}
/**
* LoadBasicChunks
*/
void LoadBasicChunks(void) {
byte *cptr;
int numObjects;
// Allocate RAM for savescene data
InitialiseSs();
InitialiseSaveScenes();
// 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
@ -581,6 +754,23 @@ void LoadBasicChunks(void) {
cptr = FindChunk(MASTER_SCNHANDLE, CHUNK_TOTAL_POLY);
if (cptr != NULL)
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 --------------------
@ -602,10 +792,39 @@ static const GameSettings tinselSettings[] = {
{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) :
Engine(syst), _gameDescription(gameDesc) {
_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
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume"));
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
@ -629,9 +848,10 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
if (native_mt32)
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
_music = new MusicPlayer(_driver);
//_music->setNativeMT32(native_mt32);
//_music->setAdlib(adlib);
_midiMusic = new MidiMusicPlayer(_driver);
_pcmMusic = new PCMMusicPlayer();
//_midiMusic->setNativeMT32(native_mt32);
//_midiMusic->setAdlib(adlib);
_musicVolume = ConfMan.getInt("music_volume");
@ -644,25 +864,38 @@ TinselEngine::TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc)
}
TinselEngine::~TinselEngine() {
if (MoviePlaying())
FinishBMV();
delete _sound;
delete _music;
delete _midiMusic;
delete _pcmMusic;
delete _console;
delete _driver;
_screenSurface.free();
FreeSs();
FreeSaveScenes();
FreeTextBuffer();
FreeHandleTable();
FreeActors();
FreeObjectList();
FreeGlobalProcesses();
FreeGlobals();
delete _scheduler;
}
Common::Error TinselEngine::init() {
// Initialize backend
initGraphics(SCREEN_WIDTH, SCREEN_HEIGHT, false);
_screenSurface.create(SCREEN_WIDTH, SCREEN_HEIGHT, 1);
if (getGameID() == GID_DW2) {
#ifndef DW2_EXACT_SIZE
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");
@ -670,6 +903,8 @@ Common::Error TinselEngine::init() {
_scheduler = new Scheduler();
InitSysVars();
// init memory manager
MemoryInit();
@ -684,20 +919,19 @@ Common::Error TinselEngine::init() {
RebootCursor();
RebootDeadTags();
RebootMovers();
resetUserEventTime();
RebootTimers();
RebootScalingReels();
DelayedScene.scene = HookScene.scene = 0;
#endif
// Load in text strings
ChangeLanguage(g_language);
// Init palette and object managers, scheduler, keyboard and mouse
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
SetupHandleTable();
@ -707,14 +941,23 @@ Common::Error TinselEngine::init() {
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 {
char filename[256];
snprintf(filename, 256, "%s.%03d", getTargetName().c_str(), saveNum);
return filename;
}
#define GAME_FRAME_DELAY (1000 / ONE_SECOND)
Common::Error TinselEngine::go() {
uint32 timerVal = 0;
@ -758,6 +1001,9 @@ Common::Error TinselEngine::go() {
// Save/Restore scene file transfers
ProcessSRQueue();
// Handle any playing movie
FettleBMV();
#ifdef DEBUG
if (bFast)
continue; // run flat-out
@ -765,6 +1011,11 @@ Common::Error TinselEngine::go() {
// Loop processing events while there are any pending
while (pollEvent());
DoCdChange();
if (MoviePlaying() && NextMovieTime())
g_system->delayMillis(MAX<int>(NextMovieTime() - g_system->getMillis() + MovieAudioLag(), 0));
else
g_system->delayMillis(10);
}
@ -776,8 +1027,11 @@ Common::Error TinselEngine::go() {
void TinselEngine::NextGameCycle(void) {
//
ChangeScene();
// Dim Music
_pcmMusic->dimIteration();
// Check for scene change
ChangeScene(false);
// Allow a user event for this schedule
ResetEcount();
@ -785,6 +1039,9 @@ void TinselEngine::NextGameCycle(void) {
// schedule process
_scheduler->schedule();
if (MoviePlaying())
CopyMovieToScreen();
else
// redraw background
DrawBackgnd();
@ -810,7 +1067,13 @@ bool TinselEngine::pollEvent() {
break;
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;
case Common::EVENT_KEYDOWN:
@ -828,7 +1091,6 @@ bool TinselEngine::pollEvent() {
/**
* Start the processes that continue between scenes.
*/
void TinselEngine::CreateConstProcesses(void) {
// Process to run the master script
_scheduler->createProcess(PID_MASTER_SCR, MasterScriptProcess, NULL, 0);
@ -841,7 +1103,6 @@ void TinselEngine::CreateConstProcesses(void) {
/**
* Restart the game
*/
void TinselEngine::RestartGame(void) {
HoldItem(INV_NOICON); // Holding nothing
@ -878,7 +1139,6 @@ void TinselEngine::RestartGame(void) {
/**
* Init palette and object managers, scheduler, keyboard and mouse.
*/
void TinselEngine::RestartDrivers(void) {
// init the palette manager
ResetPalAllocator();
@ -902,13 +1162,12 @@ void TinselEngine::RestartDrivers(void) {
}
// Set midi volume
SetMidiVolume(volMidi);
SetMidiVolume(volMusic);
}
/**
* Remove keyboard, mouse and joystick drivers.
*/
void TinselEngine::ChopDrivers(void) {
// remove sound driver
StopMidi();
@ -923,7 +1182,6 @@ void TinselEngine::ChopDrivers(void) {
/**
* Process a keyboard event
*/
void TinselEngine::ProcessKeyEvent(const Common::Event &event) {
// Handle any special keys immediately
@ -974,4 +1232,48 @@ void TinselEngine::ProcessKeyEvent(const Common::Event &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

View file

@ -28,6 +28,7 @@
#include "common/scummsys.h"
#include "common/system.h"
#include "common/error.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/util.h"
@ -39,10 +40,12 @@
#include "tinsel/debugger.h"
#include "tinsel/graphics.h"
#include "tinsel/sound.h"
#include "tinsel/dw.h"
namespace Tinsel {
class MusicPlayer;
class MidiMusicPlayer;
class PCMMusicPlayer;
class Scheduler;
class SoundManager;
@ -65,11 +68,34 @@ enum TinselGameFeatures {
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 {
TINSEL_V0 = 0, // Used in the DW1 demo only
TINSEL_V1 = 1
TINSEL_V0 = 0,
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;
enum TinselKeyDirection {
@ -79,6 +105,22 @@ enum TinselKeyDirection {
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 {
int _gameId;
Common::KeyState _keyPressed;
@ -89,11 +131,16 @@ class TinselEngine : public Engine {
Console *_console;
Scheduler *_scheduler;
static const char *_sampleIndices[][3];
static const char *_sampleFiles[][3];
static const char *_textFiles[][3];
protected:
// Engine APIs
virtual Common::Error init();
virtual Common::Error go();
virtual void syncSoundSettings();
public:
TinselEngine(OSystem *syst, const TinselGameDescription *gameDesc);
@ -109,13 +156,18 @@ public:
uint16 getVersion() const;
Common::Platform getPlatform() const;
const char *getSampleIndex(LANGUAGE lang);
const char *getSampleFile(LANGUAGE lang);
const char *getTextFile(LANGUAGE lang);
MidiDriver *_driver;
SoundManager *_sound;
MusicPlayer *_music;
MidiMusicPlayer *_midiMusic;
PCMMusicPlayer *_pcmMusic;
KEYFPTR _keyHandler;
private:
//MusicPlayer *_music;
//MidiMusicPlayer *_midiMusic;
int _musicVolume;
void NextGameCycle(void);
@ -134,7 +186,8 @@ public:
Common::Point getMousePosition() const { return _mousePos; }
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;
}
void divertKeyInput(KEYFPTR fptr) { _keyHandler = fptr; }
@ -145,6 +198,11 @@ public:
// Global reference to the TinselEngine object
extern TinselEngine *_vm;
// Externally available methods
void CuttingScene(bool bCutting);
void CDChangeForRestore(int cdNumber);
void CdHasChanged(void);
} // End of namespace Tinsel
#endif /* TINSEL_H */